String Algorithms
Chapter: 3.8 String Algorithms Level: 5 — Mastery (cross-domain: build / prove / analyze) Time limit: 90 minutes Total marks: 60
Instructions: Answer all questions. Provide rigorous proofs where asked. Pseudocode must be complete and unambiguous. Justify all complexity claims.
Question 1 — KMP Failure Function: Correctness & Amortized Analysis (24 marks)
Let be a pattern. Define the failure function
i.e. the length of the longest proper prefix of that is also a suffix of .
(a) Compute by hand for (indices –). Show the array. (4 marks)
(b) Write complete pseudocode for building in time. (4 marks)
(c) Prove the key recurrence used in construction: if we are computing and , then the candidate lengths to test are exactly the sequence down to . Justify why this sequence enumerates all borders of . (6 marks)
(d) Prove the failure-function construction runs in time using an amortized (potential) argument. Explicitly state your potential function and bound the total number of times the inner while loop executes across the whole run. (6 marks)
(e) Using , state and justify the formula for all lengths of borders of the whole string (prefixes that are also suffixes), expressed via iterating . (4 marks)
Question 2 — Rabin–Karp: Probability of a False Match (20 marks)
Rabin–Karp hashes a length- window over alphabet as
for a prime modulus .
(a) Derive the rolling-hash update formula that produces from in time. State precisely the precomputed constant needed. (4 marks)
(b) Consider text length and one pattern. Suppose is a prime chosen uniformly at random from primes . Two distinct strings collide iff divides a fixed nonzero integer with . Show that the number of prime divisors of is at most , and hence bound the probability that a fixed spurious window collides. (6 marks)
(c) Using (b) and a union bound, show the expected number of spurious (false) matches over the whole text is where is the prime-counting function. Then argue how large must be (asymptotically) so that the expected total spurious matches is , and conclude the expected running time is . (6 marks)
(d) Give one concrete adversarial input family that forces the naive matcher into worst-case time, and explain in one sentence why Rabin–Karp's worst case is still despite (c). (4 marks)
Question 3 — Manacher ⇄ Z-array: Palindromes and Physics of Symmetry (16 marks)
(a) State Manacher's algorithm's core invariant (the "mirror" reflection about the current palindrome center) and prove the initialization
where is the current center and its right boundary. Explain physically why this is exactly a reflection symmetry: relate the mirror index to a reflection map on the number line about point . (6 marks)
(b) Run Manacher (using the transformed string with separators #) on . Give the -array on the transformed string and state the longest palindromic substring and its length. (6 marks)
(c) The Z-array gives the length of the longest substring starting at that matches a prefix of . Prove that pattern occurrences of in can be found by building the Z-array of P\T$jTZ[,|P|+1+j,] = |P|$. (4 marks)
Answer keyMark scheme & solutions
Question 1
(a) For :
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| char | a | b | a | b | c | a | b | a | b | a |
| 0 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 4 | 3 |
Reasoning at : longest border of ababcababa. Prefix aba = suffix aba (length 3). Length 4 abab ≠ suffix baba, so . (4 marks: −1 per error, max 4.)
(b) Pseudocode :
build_pi(P, m):
pi[0] = 0
k = 0
for i = 1 to m-1:
while k > 0 and P[i] != P[k]:
k = pi[k-1]
if P[i] == P[k]:
k = k + 1
pi[i] = k
return pi
(4 marks: correct while-fallback 2, increment/assign 2.)
(c) Claim: the borders of a string are exactly the prefixes of lengths in the sequence terminating at .
Proof. A border of of length is a proper prefix equal to a suffix. The longest border has length . Suppose is any border with . Since the length- border is both a prefix and suffix of , and , the length- border of (being a suffix of of length ) is a suffix of ; and being a prefix of it is a prefix of . Hence any border of shorter than is precisely a border of . The longest border of has length . By induction the full set of border lengths is enumerated by repeatedly applying until reaching . This is a strictly decreasing chain (each ), so it terminates and lists every border exactly once. ∎ (6 marks: prefix∩suffix argument 3, inductive nesting of borders 2, termination/decreasing 1.)
(d) Potential argument. Let (the current matched length variable). Initially ; always .
- Each iteration of the outer
for(m−1 iterations) increases by at most (the singleifincrement). So total increase of over the whole algorithm is . - Each execution of the inner
whilebody sets , strictly decreasing by at least .
Since starts at , ends , and total increase is , the total decrease is also . Therefore the inner while body executes at most times in total across all outer iterations. Outer loop is ; total work . ∎ (6 marks: potential defined 2, increase bounded 2, decrease = while-count bounded → O(m) 2.)
(e) All border lengths of the whole string (length ): start with , then , continuing while . Each such is a length where a prefix equals a suffix of . Justification: identical to (c) applied to (the whole string), i.e. . (4 marks.)
Question 2
(a) Removing leading digit and appending trailing digit:
Precompute the constant . All operations mod ; per shift. (4 marks: subtract top 2, multiply+add new 1, precomputed constant named 1.)
(b) If two distinct length- strings have equal hash, then with and (each is in ). An integer with has at most prime divisors (each prime factor is , and the product of primes ; so ). If is uniform among the primes , the probability is one of these bad primes is
(6 marks: define D nonzero bounded 2, prime-divisor count via 2, probability ratio 2.)
(c) There are candidate windows; each non-matching window collides with probability . By union bound (linearity of expectation),
By the prime number theorem . Choosing gives , so
and choosing (or any with ) forces the expected count to . Each true match verification costs , but expected number of verifications (true + spurious) is , so total expected time is (hashing) plus per genuine occurrence. ∎ (6 marks: union bound 2, PNT & choice of N 2, conclusion O(n+m) 2.)
(d) Naive worst case: ( a's), (length , all a's then a b). At every one of the shifts the naive matcher compares matching a's before failing on the b → . Rabin–Karp worst case is still because an adversary knowing can craft inputs where every window's hash collides with the pattern hash, forcing verification at each of positions. (4 marks: example 2, RK worst-case reason 2.)
Question 3
(a) Invariant: maintain the rightmost-reaching palindrome with center and right boundary (radius so ). For , let its mirror be . Because for all (palindrome symmetry about ), the substring around mirrors that around as long as it stays inside . Hence : we can copy the mirror's radius , but not beyond the current right boundary (only characters to the right are guaranteed symmetric). Beyond we must expand explicitly.
Physical interpretation: the map is exactly a reflection (parity) about the point on the real line — the same reflection symmetry as a mirror at position . and are equidistant from on opposite sides; the palindrome property is invariance of under this reflection within radius . (6 marks: invariant + copy bound 3, reflection map as parity/mirror 3.)
(b) . Transformed (length 13, indices 0–12).
-array (radii on transformed string):
| idx | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| char | # | a | # | b | # | a | # | a | # | b | # | a | # |
| p | 0 | 1 | 0 | 3 | 0 | 1 | 6 | 1 | 0 | 3 | 0 | 1 | 0 |
Max at index 6 (the center # between the two middle a's) → longest palindrome length : substring abaaba (the whole string). (6 marks: transformed string 1, p-array correct 3, longest palindrome + length 2.)
(c) Build W = P\T$\notin P,T|P|=kjTWk+1+jZ[k+1+j]WP$...k+1+j$k$Z\le kZ[k+1+j]=kW[k+1+j \ldots k+1+j+k-1] = W[0\ldots k-1] = PT[j\ldots j+k-1]=Pkk(k{+}1)$T\iff Z[k+1+j]=k$. ∎ (4 marks: separator blocks overflow 2, iff 2.)
[
{"claim":"pi array for ababcababa is [0,0,1,2,0,1,2,3,4,3]",
"code":"P='ababcababa'\nm=len(P)\npi=[0]*m\nk=0\nfor i in range(1,m):\n while k>0 and P[i]!=P[k]:\n k=pi[k-1]\n if P[i]==P[k]:\n k+=1\n pi[i]=k\nresult = (pi==[0,0,1,2,0,1,2,3,4,3])"},
{"claim":"Rabin-Karp rolling update matches direct hash on random data (mod q, base d)",
"code":"import random\nrandom.seed(