1. 程式人生 > >BZOJ3676 APIO2014回文串(manacher+後綴自動機)

BZOJ3676 APIO2014回文串(manacher+後綴自動機)

scanf man 使用 pen getc 集合 class fail uil

  由於本質不同的回文子串數量是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; }

BZOJ3676 APIO2014回文串(manacher+後綴自動機)