最長雙回文串
阿新 • • 發佈:2018-02-05
name space 回文子串 output getc abc strlen 等於 [1]
Description
順序和逆序讀起來完全一樣的串叫做回文串。比如acbca是回文串,而abc不是(abc的順序為“abc”,逆序為“cba”,不相同)。
輸入長度為n的串S,求S的最長雙回文子串T,即可將T分為兩部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
Input
一行由小寫英文字母組成的字符串S。2≤|S|≤10^5
Output
一行一個整數,表示最長雙回文子串的長度
Sample Input
baacaabbacabb
Sample Output
12
//從第二個字符開始的字符串aacaabbacabb可分為aacaa與bbacabb兩部分,且兩者都是回文串。
首先一遍Manacher,然後記錄下兩個數組——l,r
l[i] 代表所有包含 s[i] 的回文串的最左端點(一個回文串的最右端點的 l 不等於該回文串的最左端點)
r[i] 代表所有包含 s[i] 的回文串的最右端點(一個回文串的最左端點的 r 不等於該回文串的最右端點)
有什麽用?答案就是 max{r[i]-l[i]}(1<=i<=len)
正確性?其實 r[i]-l[i] 非常好理解,關鍵是上文括號內的東西有點奇怪。其實不難,因為兩個回文串之間要有聯系,不能相互獨立,所以一個回文串的最右端點記錄的 l 不能是本身的左端點,而是其他點的值
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define inf 0x7f7f7f7f using namespace std; typedef long long ll; typedef unsigned int ui; typedef unsigned long long ull; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<‘0‘||ch>‘9‘;ch=getchar()) if (ch==‘-‘) f=-1; for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=(x<<1)+(x<<3)+ch-‘0‘; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+‘0‘); } const int N=5e5; char s[N*2+10]; int p[N*2+10],l[N*2+10],r[N*2+10]; int main(){ scanf("%s",s+1); int len=strlen(s+1),Max=0,ID=0,ans; for (int i=len;i;i--) s[i<<1]=s[i],s[i<<1|1]=‘&‘; len=len<<1|1; s[0]=‘#‘,s[1]=‘&‘,s[len+1]=‘^‘; for (int i=1;i<=len;i++){ p[i]=Max>i?min(p[ID*2-i],Max-i):1; while (s[i-p[i]]==s[i+p[i]]) p[i]++; if (Max<p[i]+i) Max=p[ID=i]+i; } for (int i=1,t=1;i<=len;i++) for (;t<i+p[i];t++) l[t]=i; //l的線性求法,很好理解,這裏便不再贅述 for (int i=len,t=len;i;i--) for (;t>i-p[i];t--) r[t]=i; //r同理 for (int i=2;i<len;i+=2) ans=max(ans,r[i]-l[i]); //求答案的時候只需要考慮讀入的字符串,不用枚舉添加的字符 printf("%d\n",ans); return 0; }
最長雙回文串