3.8.9 · Coding › String Algorithms
Intuition Ek saanс mein badi idea
Ek palindrome symmetric hota hai. Agar tumne ek bada palindrome pehle se dhundh liya hai, toh uske andar ki har cheez mirrored hai. Toh jab tum ek naye center par khade ho, tum zero se count karna shuru nahi karte — tum apni mirror position ka answer copy karte ho aur sirf us part ke liye expand karte ho jo mirror guarantee nahi kar sakta tha. Yahi reuse hai jo naive O ( n 2 ) ko == O ( n ) == mein badal deta hai.
Ek string s di gayi hai jiska length n hai, har center ke liye, wahan centered longest palindrome dhundho. Isse milta hai: longest palindromic substring, sabhi palindromic substrings ki count, etc.
Ek palindrome ke do flavours hote hain: odd length (center ek character hai, e.g. aba) aur even length (center do characters ke beech hai, e.g. abba). Dono ko handle karna thoda mushkil hai.
Intuition Odd/even headache ko khatam karna
Har character ke beech mein aur dono ends par ek separator # insert karo:
abba → #a#b#b#a#
Ab transformed string t mein har palindrome ki length odd hoti hai (uska center hamesha t ki ek single position hoti hai). s ka ek even palindrome ek # par centered odd palindrome ban jaata hai; s ka ek odd palindrome ek real char par centered palindrome ban jaata hai. Ek uniform case.
Agar t ki length m = 2 n + 1 hai, toh hum ek array == P [ i ] == compute karte hain = t[i] par centered longest palindrome ka radius (radius = t mein har side par characters ki sankhya).
Hum i ko left se right sweep karte hain aur ab tak mila sabse rightmost-reaching palindrome maintain karte hain, jise uske center C aur right boundary R se describe kiya jaata hai (toh yeh [ C − ( R − C ) , R ] cover karta hai, matlab half-length R − C ).
Maano current i known palindrome [ 2 C − R , R ] ke andar hai (matlab i < R ). i ko C ke across reflect karo:
i ′ = 2 C − i
Kyunki bada palindrome C ke baare mein symmetric hai, i ki neighbourhood bilkul uske mirror i ′ ki neighbourhood jaisi dikhti hai — jahantak bada palindrome pahunchta hai . Toh P [ i ] kam se kam P [ i ′ ] hai… lekin sirf wall R tak.
Intuition Case A: mirror palindrome strictly andar fit hota hai
Agar P [ i ′ ] < R − i , toh i ′ par palindrome bade palindrome ke bilkul andar rehta hai. Perfect symmetry se, i par palindrome ek exact copy hai: P [ i ] = P [ i ′ ] , aur yeh lambi nahi ho sakti (agar yeh expand ho sakti, toh symmetry i ′ ko bhi expand hone par force karta — contradiction). Aage koi expansion possible nahi hai.
Intuition Case B: mirror palindrome wall ko hit/exceed karta hai
Agar P [ i ′ ] ≥ R − i , toh mirror palindrome bade palindrome ke left edge tak pahunch jaata hai ya us se aage nikal jaata hai. Wall R se aage, hamare paas koi information nahi hai (symmetry guarantee R par khatam hoti hai). Toh hum sirf jaante hain P [ i ] ≥ R − i , aur hume character by character R ke aage actually expand karne ki koshish karni hogi .
Isliye hum min ( P [ i ′ ] , R − i ) lete hain: mirror par trust karo, lekin wall ke aage kabhi trust mat karo.
O ( n ) hai
min part O ( 1 ) hai. Expand loop sirf tab kaam karta hai jab yeh R ko right ki taraf push karta hai, aur R sirf badhta hai, 0 se ≤ m tak. Toh poore run mein total expansion steps ≤ m = O ( n ) hain. Amortized linear. ✨
def manacher (s):
t = '#' + '#' .join(s) + '#' # length 2n+1
m = len (t)
P = [ 0 ] * m
C = R = 0
for i in range (m):
if i < R:
P[i] = min (P[ 2 * C - i], R - i) # mirror, capped at wall
# expand
while i - P[i] - 1 >= 0 and i + P[i] + 1 < m and \
t[i - P[i] - 1 ] == t[i + P[i] + 1 ]:
P[i] += 1
# update rightmost
if i + P[i] > R:
C, R = i, i + P[i]
return P # P[i] = palindrome length in ORIGINAL s centered at t[i]
# longest palindromic substring:
def longest_pal (s):
P = manacher(s)
k = max ( range ( len (P)), key =lambda i: P[i])
start = (k - P[k]) // 2 # map t-index back to s-index
return s[start:start + P[k]]
s = "abba", t = "#a#b#b#a#" par trace (indices 0..8)
i
t[i]
mirror i ′
init P [ i ]
after expand
0
#
—
0
0
1
a
—
0 (i≥R)
1 → set C=1,R=2
2
#
0
min(0,0)=0
0
3
b
—
0
1 → C=3,R=4
4
#
i'=2, P=0
min(0,0)=0
expand! t[3]=b=t[5]=b, phir t[2]=#=t[6]=#, phir t[1]=a=t[7]=a, phir t[0]=#=t[8]=# → P=4, C=4,R=8
5
b
i'=3, P=1
min(1, R-i=3)=1
1
6
#
i'=2, P=0
min(0,2)=0
0
7
a
i'=1, P=1
min(1,1)=1
1
8
#
i'=0, P=0
min(0,0)=0
0
i=4 par kyun? Center par # hai, mirror 0 deta hai, lekin yeh R par hai, toh hume expand karna hoga — aur hum poora abba discover karte hain (P=4). Length 4 = full palindrome. ✓
i=5 par kyun? Mirror i ′ = 3 ka P = 1 tha aur P [ i ′ ] = 1 < R − i = 3 , toh Case A: hum 1 copy karte hain aur expand karne ki koshish nahi karte. Yahi speedup action mein hai.
s = "aaaa" → sab same characters wali string
t = "#a#a#a#a#". Jaise i badhta hai, har naye real-char center ka P kaafi had tak mirror se predict ho jaata hai; expansion sirf tab fire hoti hai jab yeh R ko pehle ke max se aage push kar sake. Final max P = 4 → longest palindrome aaaa. Expand loop total ~n baar chalta hai, har index ke liye nahi — linearity confirm karta hai.
Worked example Sabhi palindromic substrings count karna
t[i] par centered palindromic substrings ki sankhya ⌈ P [ i ] /2 ⌉ hai (har odd-radius layer in t ek real palindrome correspond karta hai). Sabhi i par sum karne se total count O ( n ) mein milta hai.
Common mistake "Main bas har center se expand karunga" (
O ( n 2 ) trap)
Kyun sahi lagta hai: expand-around-center simple aur correct hai. Bug: mirror reuse ke bina, aaaa...a (n a's) jaisi string Θ ( n 2 ) expansion force karti hai. Fix: Manacher P [ i ′ ] reuse karta hai toh total expansion R ke movement se bounded hai — O ( n ) .
Common mistake Mirror ko wall par cap karna bhool jaana
P[i] = P[2*C-i] likhna bina min(..., R-i) ke. Kyun sahi lagta hai: symmetry "chahiye" ki poori value copy kare. Kyun galat hai: mirror palindrome bade palindrome ke left edge ke bahar nikal sakta hai, jahan hamare paas koi symmetry guarantee nahi hai . Blindly copy karna ek aisa palindrome claim karta hai jo exist nahi karta. Fix: min(P[2*C-i], R-i), phir expand karo.
s mein wapas map karne ka galat index
k - P[k] ko directly s mein start ke roop mein use karna. Fix: yeh (k - P[k]) // 2 hai kyunki t do baar dense hai (separators). Yahan off-by-one silently galat substrings return karta hai.
Common mistake Expand loop mein boundaries ko galat handle karna
i - P[i] - 1 >= 0 aur i + P[i] + 1 < m bhoolna string ends par index errors cause karta hai. Dono ends par # padding help karta hai lekin explicit bound checks phir bhi zaroori hain.
Recall Feynman: ek 12-saal ke bachche ko explain karo
Ek row of mirror tiles ki imagine karo jahan beech mein ek magic mirror hai. Agar tumhe pehle se pata hai ki left side right side ko perfectly reflect karti hai, toh kisi jagah kya hai yeh jaanne ke liye, tum firse measure karne ke bajay uske mirror buddy ko dekh sakte ho . Tum real measuring sirf bilkul edge par karte ho jahan mirror ka reflection khatam ho jaata hai. Kyunki woh edge sirf aage move karti hai, tum kabhi same ground ko dobara measure nahi karte — isliye poora kaam fast hai. # symbols sirf spacers hain jo hum sprinkle karte hain taaki "even-length" mirrors aur "odd-length" mirrors ek jaisi shape ban jayein aur hume sirf ek tarah se deal karna pade.
"Mirror, phir Wall ko push karo."
Mirror : P[i] = min(P[2C-i], R-i) — buddy se copy karo, wall par cap karo.
Wall push karo : jab tak chars match hon expand karo; agar R se aage jaao, toh ( C , R ) move karo.
s ko #a#b#...# mein kyun transform karte hain?Yeh naye string mein har palindrome ko odd-length banne par force karta hai, odd/even cases ko unify karta hai (center hamesha ek position hoti hai).
P [ i ] kya store karta hai, aur yeh original string se kaise relate karta hai?Transformed t mein radius; numerically original s mein corresponding palindrome ki length ke barabar hai.
C center ke baare mein i ka mirror index kya hai?i ′ = 2 C − i .
i < R ke liye Manacher initialization state karo.P [ i ] = min ( P [ 2 C − i ] , R − i ) , phir expand karo.
R − i ke saath min kyun?Symmetry sirf right wall R tak guarantee karta hai; us se aage koi info nahi hai, isliye hum cap karte hain aur manually expand karte hain.
Tum kab expand NAHI karte? Jab mirror palindrome strictly bade waale ke andar fit hota hai (P [ i ′ ] < R − i ): P [ i ] = P [ i ′ ] exactly hota hai.
Tum ( C , R ) kab update karte ho? Jab i + P [ i ] > R , set karo C = i , R = i + P [ i ] .
Manacher O ( n ) kyun hai? Mirror copy O ( 1 ) hai; expansion sirf R ko aage move karta hai, aur R monotonically 0 se ≤ 2 n + 1 tak badhta hai, toh total expansion O ( n ) hai.
Transformed center index k ko s mein start index par map karo. start = (k - P[k]) // 2.
t[i] par kitne palindromic substrings centered hain?⌈ P [ i ] /2 ⌉ .
Longest Palindromic Substring (DP O ( n 2 ) vs Manacher O ( n ) )
Expand Around Center (woh naive baseline jise Manacher accelerate karta hai)
Z-Algorithm & KMP failure function (dusre linear string preprocessing jo prior work ke reuse use karte hain)
Palindromic Tree (Eertree) (distinct palindromes count karne ka alternative)
Amortized Analysis (kyun monotone R argument linear time deta hai)
builds string t of length 2n+1
radius equals length in s
Track center C and right bound R
P i = min of P i' and R - i
Longest palindrome and count