1. 程式人生 > 實用技巧 >Acwing 139. 迴文子串的最大長度(字首+字尾處理+雜湊+二分)

Acwing 139. 迴文子串的最大長度(字首+字尾處理+雜湊+二分)

地址: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\n",ac++,maxx); } }