http://poj.org/problem?id=3974
题意:求s的最长回文串。(|s|<=1000000)
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <iostream> using namespace std; char s[2000050]; int len[2000050], T; int main() { while(scanf("%s", s+1), strcmp(s+1, "END")!=0) { int n=strlen(s+1), ans=1; for(int i=n; i; --i) s[i<<1]=s[i], s[i<<1|1]=0; n=n<<1|1; s[1]=0; s[0]=1; s[n+1]=2; int cur=1; for(int i=2; i<=n; ++i) { int &now=len[i], pos=(cur<<1)-i; now=0; now=min(len[pos], cur+len[cur]-i); now=max(0, now); while(s[i-now-1]==s[i+now+1]) ++now; if(i+now>cur+len[cur]) cur=i; ans=max(ans, now); } printf("Case %d: %d ", ++T, ans); } return 0; }
学习了一下manacher= =
其实就是利用回文串的性质= =回文。。然后维护一个单调的东西= =
大概就是先处理一下串,使串变成奇数(即插入特殊字符,而且一样),这样得到的回文串都是奇数(那么半径其实就是实际的长度,即s[i-半径]~s[i+半径]是回文串)
然后从左往右扫,维护一个回文串能到达的最远的位置,中心为cur,能到达的位置为mx=s[cur+len[cur]]。那么如果i在mx前,那么i关于cur的对称点就是i'=cur*2-i。此时由于对称性,可以得到长度至少为len[i]=min(len[i'], mx-i)。然后再继续向两边拓展,且如果位置大于了mx,记得更新。
可以证明这样是均摊$O(n)$的...