Acwing 139. 迴文子串的最大長度(字首+字尾處理+雜湊+二分)
阿新 • • 發佈:2020-11-12
地址: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; constint 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\n",ac++,maxx); } }