= the length of the longest proper border of — a string that is both a proper prefix and a suffix of that prefix.
Computation —
vector<int> prefixFunction(const string& s) {
int n = s.size();
vector<int> pi(n, 0);
for (int i = 1; i < n; i++) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1]; // fall back along borders
if (s[i] == s[j]) j++;
pi[i] = j;
}
return pi;
}Why it is linear: increases by at most 1 per iteration ( total increases) and the while loop only decreases it, so the total number of decreases is also . The same amortization as Kasai and the monotonic stack.
Pattern matching
Run the prefix function on pattern + '#' + text, where # occurs in neither. Every position where marks an occurrence.
vector<int> findAll(const string& text, const string& pat) {
string s = pat + '\x01' + text;
vector<int> pi = prefixFunction(s), res;
for (int i = pat.size() + 1; i < (int)s.size(); i++)
if (pi[i] == (int)pat.size())
res.push_back(i - 2 * pat.size());
return res;
}The separator is essential — without it, could exceed and match across the boundary.
Memory-light alternative: run the matching loop directly against the text without concatenating, keeping only the pattern’s array. That is the classic KMP formulation; see KMP.
What gives you
| Question | Answer |
|---|---|
| Smallest period of | |
| Is a repetition of a shorter string? | divides and |
| All borders of | the chain |
| Occurrences of a pattern | as above |
| Number of occurrences of each prefix in | count backwards over (see below) |
| Shortest string with as both a prefix and a suffix | |
| Minimum characters to append to make a palindrome | of |
Counting prefix occurrences
vector<int> cnt(n + 1, 0);
for (int i = 0; i < n; i++) cnt[pi[i]]++;
for (int i = n - 1; i > 0; i--) cnt[pi[i-1]] += cnt[i]; // propagate along borders
for (int i = 0; i <= n; i++) cnt[i]++; // each prefix occurs as itself
// cnt[len] = number of occurrences of the prefix of length lenThe KMP automaton
Precompute = the state after reading character in state . That turns matching into a table lookup per character, and — more importantly — makes the pattern into a DFA you can run a DP over.
vector<array<int,26>> aut(n + 1);
for (int i = 0; i <= n; i++)
for (int c = 0; c < 26; c++) {
if (i > 0 && c != s[i] - 'a') aut[i][c] = aut[pi[i-1]][c];
else aut[i][c] = i + (c == s[i] - 'a');
}This is the key to problems like “count strings of length that do not contain the pattern” — a DP over (position, automaton state), often combined with matrix exponentiation when is huge.
Prefix function vs Z-function
| Prefix function | Z-function | |
|---|---|---|
| / means | longest border of the prefix ending at | longest common prefix of and |
| Natural for | periods, borders, automata | pattern matching, string comparison |
| Conversions | each is computable from the other in |
Both are and both solve pattern matching. Learn one well; the Z-function is slightly easier to reason about, the prefix function extends to automata and Aho-Corasick.
See also: Z-Function · KMP · String Fundamentals