BZOJ3676 APIO2014回文串(manacher+后缀自动机)
由于本质不同的回文子串数量是O(n)的,考虑在对于每个回文子串在第一次找到它时对其暴力统计。可以发现manacher时若右端点移动则找到了一个新回文串。注意这样会漏掉串长为1的情况,特判一下。
现在问题变为统计一个子串的出现次数。可以用SA,二分乱搞一下即可。这里使用SAM。以parent树上表示该子串的节点为起点,用倍增往上跳,找到深度最小的满足len限制的点就好了,出现次数就是其right集合的大小。
uojAC,luoguRE一个点,bzojMLE……
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 600010 int n,last,cnt,len[N],fail[N],son[N][26],size[N],pos[N>>1],p[N]; long long ans=0; char s[N]; namespace tree { int p[N],t=0,fa[N][20]; struct data{int to,nxt; }edge[N]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void dfs(int k) { for (int i=p[k];i;i=edge[i].nxt) { fa[edge[i].to][0]=k; dfs(edge[i].to); size[k]+=size[edge[i].to]; } } void build() { for (int i=2;i<=cnt;i++) addedge(fail[i],i); fa[1][0]=1;dfs(1); for (int j=1;j<20;j++) for (int i=1;i<=cnt;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; } int calc(int l,int r) { int x=pos[r]; for (int j=19;~j;j--) if (len[fa[x][j]]>=r-l+1) x=fa[x][j]; return size[x]; } } using tree::calc; void ins(int c,int n) { int x=++cnt,p=last;last=x;len[x]=n;size[x]=1;pos[n]=x; while (!son[p][c]&&p) son[p][c]=x,p=fail[p]; if (!p) fail[x]=1; else { int q=son[p][c]; if (len[p]+1==len[q]) fail[x]=q; else { int y=++cnt;len[y]=len[p]+1; memcpy(son[y],son[q],sizeof(son[q])); fail[y]=fail[q];fail[q]=fail[x]=y; while (son[p][c]==q) son[p][c]=y,p=fail[p]; } } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3676.in","r",stdin); freopen("bzoj3676.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif last=cnt=1; scanf("%s",s+1);n=strlen(s+1); for (int i=1;i<=n;i++) ins(s[i]-97,i); tree::build(); for (int i=n;i>=1;i--) s[i*2-1]=s[i]; for (int i=1;i<n;i++) s[i<<1]='$'; int x=1; for (int i=1;i<=n;i++) ans=max(ans,1ll*calc(i,i)); for (int i=2;i<n*2;i++) { if (x+p[x]>i) p[i]=min(x+p[x]-i,p[x-(i-x)]); while (i-p[i]-1>=1&&i+p[i]+1<n*2&&s[i+p[i]+1]==s[i-p[i]-1]) { p[i]++; if (s[i+p[i]]!='$') ans=max(ans,1ll*((i+p[i]>>1)-(i-p[i]>>1)+1)*calc((i-p[i]>>1)+1,(i+p[i]>>1)+1)); } if (i+p[i]>x+p[x]) x=i; } cout<<ans; return 0; }