= the length of the longest common prefix of and the suffix . By convention (or 0; be consistent).
s = a a b c a a b x a a a z
z = _ 1 0 0 3 1 0 0 2 2 1 0
Computation —
Maintain the Z-box : the rightmost interval known to match a prefix of .
vector<int> zFunction(const string& s) {
int n = s.size();
vector<int> z(n, 0);
z[0] = n;
for (int i = 1, l = 0, r = 0; i < n; i++) {
if (i < r) z[i] = min(r - i, z[i - l]); // reuse previous work
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) z[i]++;
if (i + z[i] > r) { l = i; r = i + z[i]; } // extend the Z-box
}
return z;
}Why linear: the while loop only runs when it pushes further right, and never decreases — so the total number of character comparisons is .
Why min(r - i, z[i - l]): inside the Z-box, equals , so starts at — capped at , because beyond nothing is known yet.
Pattern matching
vector<int> findAll(const string& text, const string& pat) {
string s = pat + '\x01' + text;
vector<int> z = zFunction(s), res;
for (int i = pat.size() + 1; i < (int)s.size(); i++)
if (z[i] >= (int)pat.size())
res.push_back(i - pat.size() - 1);
return res;
}time and memory. The separator must not appear in either string.
What the Z-function answers
| Question | Method |
|---|---|
| All occurrences of a pattern | as above |
| Number of distinct substrings | add characters one at a time, using of the reversed prefix |
| Longest common prefix of and | of |
| Is a rotation of ? | search in |
| Smallest period | smallest with |
| Compare with | one query plus one character |
| String compression / repeated blocks | check with and |
Counting distinct substrings incrementally
Adding a character to the end of creates new substrings (the suffixes of the new string), minus those that already occurred. Computing the Z-function of the reversed new string gives , the length of the longest new suffix that already appeared, so the count increases by . Total — good enough when and much simpler than a suffix structure.
Z-function vs prefix function
They are interconvertible in and both solve matching in linear time. Practical differences:
| Z-function | Prefix function | |
|---|---|---|
| Meaning | LCP with the whole string | longest border of a prefix |
| Easier to derive from scratch | ✔ | |
| Extends to an automaton | ✔ | |
| Extends to multi-pattern | ✔ | |
| Gives periods directly | needs a scan | ✔ immediately |
Use the Z-function for matching and comparison; the prefix function when you need borders, periods, or an automaton to run a DP over.
The Z-box idea generalises
“Maintain the rightmost known-matching interval and reuse it” is exactly the technique behind Manacher’s algorithm for palindromes. Recognising the shared skeleton makes both easier to remember: keep an interval, reuse the mirrored answer, extend naively only past the boundary.
See also: Prefix Function · Z-Algorithm · Manacher