We need to find the longest proper prefix of a string that is also a suffix of that same string. "Proper" means we can't use the entire string itself, so for "abc", valid prefixes are "a" and "ab", and valid suffixes are "c" and "bc". None match, so the answer is empty.
Example 2 shows that the prefix and suffix can overlap. In "ababab", the prefix "abab" (characters 0-3) and the suffix "abab" (characters 2-5) share characters 2 and 3. That is allowed. We are not looking for non-overlapping matches, only a prefix that equals a suffix.
This is the same quantity the KMP algorithm's LPS (Longest Prefix Suffix) array computes at each position. The last entry of the LPS array is the length of the longest proper prefix that is also a suffix of the entire string, which is exactly what this problem asks for.
1 <= s.length <= 10^5: The string can be up to 100,000 characters. An O(n^2) brute force would mean up to 10^10 character comparisons in the worst case, which is too slow. We need O(n) or O(n log n).s contains only lowercase English letters, 26-character alphabet, which is relevant for rolling hash approaches where we need a base and modulus.s.length >= 1: We need to handle single-character strings, where the answer is always empty (no proper prefix exists).Try every possible prefix length from longest to shortest, and for each one, check whether the prefix equals the corresponding suffix. Because we check from longest to shortest, the first match we find is the answer.
For a string of length n, the valid prefix lengths range from n-1 (the longest proper prefix) down to 1. For each candidate length k, we compare the first k characters with the last k characters. If they're identical, that's our answer.
n be the length of s.k from n - 1 down to 1:s[0..k-1] with s[n-k..n-1].s[0..k-1].Loading animation...
With n up to 100,000, this is too slow. The next approach replaces the character-by-character comparison with an O(1) hash comparison.
A polynomial hash maps a string to a single number, a fingerprint. If two strings have different fingerprints they are different; if the fingerprints match they are equal with high probability. We maintain two running hashes as we scan: one for the prefix growing from the left, one for the suffix growing from the right. At each length k, if the two hashes match, length k is a candidate happy prefix.
The reason this beats brute force is that each extension updates both hashes in O(1) arithmetic, so the full scan is O(n) instead of O(n^2).
Two different strings can hash to the same value, a collision. With a single large prime modulus near 10^9, the chance that two unequal strings collide is roughly 1/mod, about 10^-9. A production-grade solution verifies a matched length by comparing the actual characters, or uses two independent moduli to push the collision probability to about 10^-18. The code below uses a single hash, which is enough for these inputs.
The loop runs i from 0 to n - 2, not n - 1, because we want proper prefixes. A prefix of length n is the whole string, which the problem excludes.
prefixHash = 0, suffixHash = 0, power = 1, and longestLen = 0.i from 0 to n - 2:prefixHash = prefixHash * base + s[i].suffixHash = s[n - 1 - i] * power + suffixHash.power = power * base.prefixHash == suffixHash, set longestLen = i + 1.s[0..longestLen-1].Loading animation...
Rolling hash gives O(n) time but carries a small chance of a hash collision producing a wrong answer. The next approach, based on KMP, is O(n) and deterministic with no collision risk.
The KMP algorithm's preprocessing step builds an LPS (Longest Prefix Suffix) array, where lps[i] stores the length of the longest proper prefix of s[0..i] that is also a suffix of s[0..i]. The answer to this problem is lps[n - 1], the LPS value for the whole string. If lps[n - 1] = k, the first k characters of s equal the last k characters.
The LPS array is built with a single pointer len that tracks how much of the current prefix-suffix has matched. When the next character extends the match, len increases by one. On a mismatch, instead of restarting, len falls back to lps[len - 1], the length of the next-shorter prefix that is also a suffix of what has matched so far. That fallback is what keeps the algorithm linear.
The fallback len = lps[len - 1] is correct because lps[len - 1] is the length of the longest proper prefix that is also a suffix of the first len matched characters. That is exactly the next-best candidate to try after the current match fails, so no shorter valid candidate is skipped.
The total cost is O(n) by amortized analysis. len increases by one at most n times across the whole run, since i advances on every increment and i reaches n only once. Each fallback strictly decreases len, and len never goes below 0, so the total number of decreases cannot exceed the total number of increases. Both are bounded by n, so the loop does O(n) work overall.
lps of size n, initialized to all zeros.len = 0 (length of the current matched prefix-suffix) and i = 1.i < n:s[i] == s[len], set lps[i] = len + 1, increment both len and i.len > 0, fall back: set len = lps[len - 1] (don't increment i).lps[i] = 0 and increment i.s[0..lps[n-1]-1].Loading animation...
len increases at most n-1 times and decreases at most n-1 times.