Palindrome--POJ3974
【题目概述】如果一个字符串正着读和倒着读是一样的,则称它是回文的。给定一个长度为N的字符串S,求它的最长回文子串。
分为两类:
1.奇回文串A【1~M】,长度M为奇数,并且A【1~M/2+1】=reverse(A【M/2+1~M】),它的中心是一个字符。其中reverse(A)表示把字符串A倒过来。
2.偶回文串B【1~M】,长度M为偶数,且B【1~M/2】=reverse(B【M/2+1~M】),它的中心点是两个字符之间的夹缝。
于是,我们正反各求一次哈希,P取131或13331,M取264,也就是直接用unsigned long long类型存储这个hash值。
###正着求一次哈希值
1 ull H1(int i,int j){ 2 return f1[j]-f1[i-1]*p[j-i+1]; 3 }
###倒着求一次哈希值
1 ull H2(int i,int j){ 2 return f2[i]-f2[j+1]*p[j-i+1]; 3 }
###注意读入的写法,遇到END结束。
1 while(scanf("%s",s+1)&&(s[1]!='E'))
###预处理出p的各个次方
1 p[0]=1; 2 for(int i=1;i<=N;i++) 3 p[i]=p[i-1]*P;
枚举回文子串的中心位置i(1~N),看从这个中心位置出发向左右两侧最长可以扩展出多长的回文串。利用二分答案的思想,二分长度,再以中间点i计算长度为mid的两个子串的哈希值,如果相等,调整左指针,否则调整右指针。
在枚举过的所有中心位置对应的奇、偶回文子串长度中取max就是整道题目的答案,时间复杂度为O(NlogN)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ull unsigned long long 6 using namespace std; 7 const int N=1000010,P=131; 8 char s[N]; 9 ull f1[N],f2[N],p[N]; 10 ull H1(int i,int j){ 11 return f1[j]-f1[i-1]*p[j-i+1]; 12 } 13 ull H2(int i,int j){ 14 return f2[i]-f2[j+1]*p[j-i+1]; 15 } 16 int main(){ 17 int id=0; 18 p[0]=1; 19 for(int i=1;i<=N;i++) 20 p[i]=p[i-1]*P; 21 while(scanf("%s",s+1)&&(s[1]!='E')){ 22 ++id; 23 int ans=0,len=strlen(s+1); 24 for(int i=1;i<=len;i++) f1[i]=f1[i-1]*P+s[i]; 25 for(int i=len;i;i--) f2[i]=f2[i+1]*P+s[i]; 26 for(int i=1;i<=len;i++){ 27 int l=1,r=min(i-1,len-i); 28 while(l<r){ 29 int mid=(l+r+1)>>1; 30 if(H1(i-mid,i-1)==H2(i+1,i+mid)) l=mid; 31 else r=mid-1; 32 } 33 ans=max(l*2+1,ans); 34 l=1,r=min(i-1,len-i+1); 35 while(l<r){ 36 int mid=(l+r+1)>>1; 37 if(H1(i-mid,i-1)==H2(i,i+mid-1)) l=mid; 38 else r=mid-1; 39 } 40 ans=max(l*2,ans); 41 } 42 printf("Case %d: %d\n",id,ans); 43 } 44 return 0; 45 46 }

浙公网安备 33010602011771号