地址:https://www.acwing.com/problem/content/141/
解析:
1:在假设完全无差错的情况下,不同子串对应不同哈希值。
对于abcba,那么ab的哈希值应该等于ba的反转哈希值。
也就是说,根据一个字符串,迅速求出它的逆序字符串的反哈希值,如果两者相等,则两者为正逆序的关系。
所以要处理两个哈希,一个前缀哈希和一个后缀哈希。
2:每次处理一个串,它分两种情况,长度为偶数和奇数,很明显,奇数的时候,我们只需要根据中点 ,就可以直接判断以它为中点的两个半径是否相等。而偶数相对麻烦一点,所以考虑把字符串括一下,每两个字符之间加入同一个26个字母之外的一个字符('z'+1即可),这样,我们枚举所有包含原字母的串,它们都是奇数长度。(一个非常好的技巧~)
3:枚举每一个中点,二分半径,即可在O(nlogn)的复杂度下ac了。
#include<cstdio> #include<cstring> #include<vector> #include<set> #include<stack> #include<algorithm> #include<iostream> #include<vector> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 1e6+10,P=131;//131 or 13331¾ÑéÖµ ull hl[2*maxn],hr[2*maxn],p[2*maxn]; int le; char st[2*maxn]; void init() { p[0]=1; for(int i=1;i<=le;i++)//正缀 { p[i]=p[i-1]*P; hl[i]=hl[i-1]*P+st[i]-'a'+1; } for(int i=le;i>=1;i--)//后缀 { hr[i]=hr[i+1]*P+st[i]-'a'+1; } } ull checkl(int l,int r) { return hl[r]-hl[l-1]*p[r-l+1]; } ull checkr(int l,int r) { return hr[l]-hr[r+1]*p[r-l+1];//因为与正缀求法是相反的。 } int main() { int ac=1; while(scanf("%s",st+1)) { if(!strcmp(st+1,"END")) break; le=strlen(st+1); for(int i=2*le;i>=1;i-=2)//扩充 { st[i]=st[i/2]; st[i-1]='z'+1; } le=2*le; init(); int maxx=0; for(int i=1;i<=le;i++) { int l=0 ,r=min(i-1,le-i);//最大半径 while(l<r) { int md = l+r+1>>1; if(checkl(i-md,i-1)!=checkr(i+1,i+md))//如果不相等,则半径值大了,r=md-1让它变小。 { r=md-1; } else l=md; } int ml=l*2+1; if(st[i+l+1]=='{'||i+l+1>le) //根据尾部+1处判断实际长度,这个举个例子就可以推出 ml=ml-ml/2; else ml=ml-ml/2-1; maxx=max(maxx,ml); } printf("Case %d: %d ",ac++,maxx); } }