Poj3974 最长回文子串

来源:互联网 发布:谷歌输入法 mac 编辑:程序博客网 时间:2024/06/05 09:40
 

 题目链接: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 ofS 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 allP[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 anyS[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 1000008char 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;}