题目链接:http://poj.org/problem?id=3974
最长回文子串问题,有一个O(n)的算法叫Manacher,
http://zhuhongcheng.wordpress.com/2009/08/02/a-simple-linear-time-algorithm-for-finding-longest-palindrome-sub-string/
原文:
Given a string S, we are to find the longest sub-string s of S such that the reverse of s is exactly the same as s.
First insert a special character ‘#’ between each pair of adjacent characters of S, in front of S and at the back of S. After that, we only need to check palindrome sub-strings of odd length.
Let P[i] be the largest integer d such that S[i-d,...,i+d] is a palindrome. We calculate all P[i]s from left to right. When calculating P[i], we have to compare S[i+1] with S[i-1], S[i+2] with S[i-2] and so on. A comparison is successful if two characters are the same, otherwise it is unsuccessful. In fact, we can possibly skip some unnecessary comparisons utilizing the previously calculated P[i]s.
Assume P[a]+a=max{ P[j]+j : j<i }. If P[a]+a >= i, then we have
P[i] >= min{ P[2*a-i], 2*a-i-(a- P[a])}.
Is it the algorithm linear time? The answer is yes.
First the overall number of unsuccessful comparisons is obviously at most N.
A more careful analysis show that S[i] would never be compared successfully with any S[j](j<i) after its first time successful comparison with some S[k] (k<i).
So the number of overall comparisons is a most 2N.
Here is a simple example, for string “a#b#a#a#b#a”. Hope it will help you.
At position #0: P[0]=0, the longest palindrome is “a”.
At position #1: max of {P[0]+0} is 0, so we just check the “a#b” to determine P[1]. It turns out that P[1]=0
At position #2: max of { P[0]+0, P[1]+1} is P[1]+1=1, so we just check “#b#”,”a#b#a” to determine P[1]. It turns out that P[2]=2.
At position #3: max of { P[0]+0,P[1]+1,P[2]+2} is P[2]+2=4. We can know that p[3]>=Min{ P[2*2-3], 2*2-3-(2-P[2]) } = 0, so we check “b#a” to determine that P[3]=0
At position #4: max of { P[0]+0,P[1]+1,P[2]+2,P[3]+3} is P[2]+2=4. So P[4]>=Min{ P[2*2-4], 2*2-4-(2-P[2]) } = 0, we check “#a#” to determine that P[4]=1
At postion #5: max of { P[0]+0,P[1]+1,P[2]+2,P[3]+3, P[4]+4} is P[4]+4=5, so P[5]>=Min{P[2*4-5], 2*4-5-(4-P[4]) }=0, we check “a#a”,…,”a#b#a#a#b#a” to determine that P[5]=5.
At position #6: max of {P[0]+0,P[1]+1,P[2]+2,P[3]+3, P[4]+4, P[5]+5} is P[5]+5=10, so P[6]>=Min{ P[2*5-6], 2*5-6-(5-P[5])} =1, we check “a#a#b” to determine that P[6]=1. (here we don’t need to check “#a#”)
At position #7: max of {P[0]+0,P[1]+1,P[2]+2,P[3]+3, P[4]+4, P[5]+5,P[6]+6} is P[5]+5=10, so P[7]>=Min{ P[2*5-7], 2*5-7-(5-P[5]) }=0, we check “a#b” to determine that P[7]=0.
At position #8: max of {P[0]+0,P[1]+1,P[2]+2,P[3]+3, P[4]+4, P[5]+5,P[6]+6,…} is P[5]+5=10, so P[8]>=Min{ P[2*5-8], 2*5-8-(5-P[5])} =2,
we don’t need check any substring due to the boundary.
Code:
#include<stdio.h> #include<string.h> #define M 1000008 char o[M],s[M<<1]; int p[M<<1]; int r; int MMin(int a,int b) { return a>b?b:a; } void Manacher() { int i,j,max; int n=strlen(o); memset(s,'#',sizeof(s)); for(i=0;i<n;i++) s[(i+1)<<1]=o[i]; s[n=(n+1)<<1]=0; r=max=0; for(i=0;i<n;i++){ if(max>i) p[i]=MMin(p[2*j-i],max-i); else p[i]=1; while(s[i+p[i]]==s[i-p[i]]) p[i]++; if(p[i]>r) r=p[i]; if(i+p[i]>max) max=i+p[i],j=i; } } int main() { int t=0; while(~scanf("%s",o),o[0]^'E'){ Manacher(); printf("Case %d: %d\n",++t,r-1); } return 0; }