bzoj1396 识别子串
题解:
好神啊
建出后缀自动机,然后处理每个点parent树子树中endpos的数量,若为1即可成为识别子串。
对于每个点维护原串所在位置以及right的最小值mn。
然后套上线段树维护区间最小值。
基本就这样了。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100050 char s[N]; struct Point { int len,pre,trs[28],pla; }p[2*N]; int hed[2*N],cnt,n; struct EG { int to,nxt; }e[2*N]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } int mn[2*N],siz[2*N]; struct SAM { int tot,las; SAM(){tot=las=1;} void insert(int c) { int np,nq,lp,lq; np=++tot; siz[np] = 1; p[np].len = p[las].len + 1; p[np].pla = p[las].pla + 1; for(lp = las;lp&&!p[lp].trs[c];lp=p[lp].pre) p[lp].trs[c] = np; if(!lp)p[np].pre = 1; else { lq = p[lp].trs[c]; if(p[lq].len == p[lp].len+1)p[np].pre = lq; else { nq = ++tot; p[nq] = p[lq]; p[nq].len = p[lp].len+1; p[lq].pre = p[np].pre = nq; while(p[lp].trs[c]==lq) { p[lp].trs[c] = nq; lp = p[lp].pre; } } } las = np; } void dfs(int u){for(int j=hed[u];j;j=e[j].nxt)dfs(e[j].to),siz[u]+=siz[e[j].to];} void build() { for(int i=2;i<=tot;i++) { mn[i] = p[p[i].pre].len+1; ae(p[i].pre,i); } dfs(1); } }sam; int ans[N]; struct segtree { int v[N<<2]; void pushdown(int u) { v[u<<1]=min(v[u<<1],v[u]); v[u<<1|1]=min(v[u<<1|1],v[u]); } void build(int u,int l,int r) { v[u]=n; if(l==r)return ; int mid = (l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } void insert(int u,int l,int r,int ql,int qr,int d) { if(l==ql&&r==qr) { v[u] = min(v[u],d); return ; } pushdown(u); int mid = (l+r)>>1; if(qr<=mid)insert(u<<1,l,mid,ql,qr,d); else if(ql>mid)insert(u<<1|1,mid+1,r,ql,qr,d); else insert(u<<1,l,mid,ql,mid,d),insert(u<<1|1,mid+1,r,mid+1,qr,d); } void down(int u,int l,int r) { if(l==r) { ans[l]=v[u]; return ; } pushdown(u); int mid = (l+r)>>1; down(u<<1,l,mid); down(u<<1|1,mid+1,r); } }tr; int main() { scanf("%s",s+1); n = strlen(s+1); for(int i=1;i<=n;i++) sam.insert(s[i]-'a'+1); sam.build(); tr.build(1,1,n); for(int i=1;i<=sam.tot;i++) { if(siz[i]==1) { tr.insert(1,1,n,p[i].pla-mn[i]+1,p[i].pla,mn[i]); } } tr.down(1,1,n); for(int i=2;i<=n;i++)ans[i] = min(ans[i],ans[i-1]+1); for(int i=n-1;i>=1;i--)ans[i] = min(ans[i],ans[i+1]+1); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }