题目描述
如果一个字符串正着读和倒着读是一样的,则称它是回文的。
给定一个长度为N的字符串S,求他的最长回文子串的长度是多少
输入
输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个小写字符的形式给出。
输入以一个以字符串“END”(不包括引号)开头的行表示输入终止。
直接中文翻译,就是爽。
分析
普通思路,字符串hash+二分答案。
先观察一下回文串的性质,一般的,可以分为奇数串和偶数串,奇回文串A,顾名思义,长度为奇数M,所以它的中心点是一个字符,并且
偶回文串A,就是长度为偶数M,它的中心点就是两个字符之间的夹缝,并且
所以在本题中,我们可以枚举回文子串的中心位置,看这个中心位置能向两边延伸多长。
直接搬代码
#include<bits/stdc++.h> using namespace std; #define uint unsigned long long int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();} return x*f; } const int N=1e6+10,P=13331; char s[N]; uint f1[N];//顺着 uint f2[N];//倒着 uint p[N];//次方 uint H1(int x,int y)//正着 { return (f1[y]-f1[x-1]*p[y-x+1]); } uint H2(int x,int y)//倒着 { return (f2[x]-f2[y+1]*p[y-x+1]); } signed main() { int cnt=0; p[0]=1; for(int i=1;i<N;i++) p[i]=p[i-1]*P; while(scanf("%s",s+1)&&!(s[1]=='E'&&s[2]=='N'&&s[3]=='D')) { ++cnt; int len=strlen(s+1); int ans=0; f2[len+1]=0; for(int i=1;i<=len;i++) f1[i]=f1[i-1]*P+s[i]; for(int i=len; i;i--) f2[i]=f2[i+1]*P+s[i]; for(int i=1;i<=len;i++) { int l=0;//二分答案 int r=min(i-1,len-i); //cout<<"偶"<<l<<" "<<r<<endl; while(l<r) { int mid=(l+r+1)>>1;//此mid就是书上的p if(H1(i-mid,i-1)==H2(i+1,i+mid))//偶回文子串 l=mid; else r=mid-1; } ans=max(l*2+1,ans); l=0,r=min(i-1,len-i+1); //cout<<"奇"<<l<<" "<<r<<endl; while(l<r) { int mid=(l+r+1)>>1;//此mid就是书上的q if(H1(i-mid,i-1)==H2(i,i+mid-1))//奇回文子串 l=mid; else r=mid-1; } ans=max(l*2,ans); } printf("Case %d: %d\n",cnt,ans); } return 0; }
你以为这就完了,不可能,我们还可以优化那么一下下,你看啊,如果就找上面的做法,我们需要分别处理奇回文串和偶回文串的问题,要进行两次二分答案,这样就很烦,因为二分本来就不好写,一不留神就写错了,还不容易发现,那有什么办法呢。
我们可以考虑在每一个字符后面都加一个#(其实啥都可以,只要不影响判断原本的字符就行了),加了字符后,我们就会惊奇地发现,每一个字符串都变成奇数了(其实就是简单的奇加偶得奇的事),那这样我们不就好办了,同样直接上代码
#include<bits/stdc++.h> using namespace std; #define uint unsigned long long int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();} return x*f; } const int N=2e6+10,P=131; char s[N]; uint f1[N]; uint f2[N]; uint p[N]; uint H(uint h[],int l,int r) { return h[r]-h[l-1]*p[r-l+1]; } signed main() { int T=1; while(scanf("%s",s+1)&&strcmp(s+1,"END")) { int len=strlen(s+1); for(int i=len*2;i;i-=2) { s[i]=s[i/2]; s[i-1]='z'+1; } len*=2; p[0]=1; for(int i=1,j=len;i<=len;i++,j--) { p[i]=p[i-1]*P; f1[i]=f1[i-1]*P+s[i]-'a'+1; f2[i]=f2[i-1]*P+s[j]-'a'+1; } int ans=0; for(int i=1;i<=len;i++) { int l=0; int r=min(i-1,len-i); while(l<r) { int mid=(l+r+1)>>1; if(H(f1,i-mid,i-1)!=H(f2,len-(i+mid)+1,len-(i+1)+1)) r=mid-1; else l=mid; } if(s[i-l]<='z') ans=max(ans,l+1); else ans=max(ans,l); } printf("Case %d: %d\n",T++,ans); } return 0; }
我发现这是这题唯一的题解,赶紧抓紧机会,都看到这了,不点个赞?