bzoj 1396: 識別子串 (字尾自動機+線段樹)
阿新 • • 發佈:2019-02-19
1396: 識別子串
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 308 Solved: 190
[Submit][Status][Discuss]
Description
Input
一行,一個由小寫字母組成的字串S,長度不超過10^5Output
L行,每行一個整數,第i行的資料表示關於S的第i個元素的最短識別子串有多長.Sample Input
agoodcookcooksgoodfoodSample Output
12
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4
HINT
Source
題解:字尾自動機+線段樹
這道題可以用來更新答案的狀態一定的是|right|的大小等於1的狀態,每個節點都對應著一些長度為[l[fa]+1,l[x]]的子串.我們按照拓撲序更新,並且需要維護right集合中右端點的位置st[x],然後對於每個|right|=1的狀態,對於原串中st[x]-l[fa],st[x]位置都可以用l[fa]+1來更新最小值,如果只是這麼做的話,我們發現更新到的位置十分有限,但是我們無法對於[l[fa]+1,l[x]]中的長度都進行更新。於是考慮什麼情況是隻用上面的方法更新不到的。
舉例說明bbbc 對於第三個位置答案應該是bc,但是c在更新的時候只用1更新了自己,而第三個b在更新的時候用3更新了[1,3]這個區間。對於這種情況,我們用線段樹再維護一個值,就是能更新到這個點的離他最近的點的位置,即用st[x]更新[st[x]-l[x]+1,st[x]-l[fa]-1]最後計算出距離更新答案即可。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define N 200003 using namespace std; int fa[N],ch[N][30],l[N],pos[N],rt[N],v[N],n,inf; int p,np,nq,q,last,root,cnt,delta[N*4],tr[N*4],st[N]; char s[N]; void extend(int x) { int c=s[x]-'a'; p=last; np=last=++cnt; l[np]=l[p]+1; for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; if (!p) fa[np]=root; else{ q=ch[p][c]; if (l[p]+1==l[q]) fa[np]=q; else { nq=++cnt; l[nq]=l[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q]; fa[q]=fa[np]=nq; for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } } void pushdown(int now) { if (delta[now]!=inf) { tr[now<<1]=min(tr[now<<1],delta[now]); tr[now<<1|1]=min(tr[now<<1|1],delta[now]); delta[now<<1]=min(delta[now<<1],delta[now]); delta[now<<1|1]=min(delta[now<<1|1],delta[now]); delta[now]=inf; } } void qjchange(int now,int l,int r,int ll,int rr,int v) { if (ll<=l&&r<=rr) { tr[now]=min(tr[now],v); delta[now]=min(delta[now],v); return; } int mid=(l+r)/2; pushdown(now); if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v); if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v); } int find(int now,int l,int r,int x) { if (l==r) return tr[now]; int mid=(l+r)/2; pushdown(now); if (x<=mid) return find(now<<1,l,mid,x); else return find(now<<1|1,mid+1,r,x); } namespace ac{ int delta[N*4],tr[N*4]; void clear() { memset(delta,127,sizeof(delta)); memset(tr,127,sizeof(tr)); } void pushdown(int now) { if (delta[now]!=inf) { tr[now<<1]=min(tr[now<<1],delta[now]); tr[now<<1|1]=min(tr[now<<1|1],delta[now]); delta[now<<1]=min(delta[now<<1],delta[now]); delta[now<<1|1]=min(delta[now<<1|1],delta[now]); delta[now]=inf; } } void qjchange(int now,int l,int r,int ll,int rr,int v) { if (ll<1||rr<1) return; if (ll<=l&&r<=rr) { tr[now]=min(tr[now],v); delta[now]=min(delta[now],v); return; } int mid=(l+r)/2; pushdown(now); if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v); if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v); } int find(int now,int l,int r,int x) { if (l==r) return tr[now]; int mid=(l+r)/2; pushdown(now); if (x<=mid) return find(now<<1,l,mid,x); else return find(now<<1|1,mid+1,r,x); } } int main() { freopen("a.in","r",stdin); freopen("my.out","w",stdout); scanf("%s",s+1); n=strlen(s+1); root=last=++cnt; for (int i=1;i<=n;i++) extend(i); for (int i=1;i<=cnt;i++) v[l[i]]++; for (int i=1;i<=n;i++) v[i]+=v[i-1]; for (int i=cnt;i>=1;i--) pos[v[l[i]]--]=i; p=root; for (int i=1;i<=n;i++) { int c=s[i]-'a'; p=ch[p][c]; rt[p]=1; st[p]=i; //cout<<c<<" "<<p<<endl; } memset(tr,127,sizeof(tr)); memset(delta,127,sizeof(delta)); ac::clear(); inf=tr[0]; for (int i=cnt;i>=1;i--) { int mn=l[fa[pos[i]]]+1; int mn1=l[pos[i]]; rt[fa[pos[i]]]+=rt[pos[i]]; st[fa[pos[i]]]=st[pos[i]]; //cout<<st[pos[i]]<<" "<<l[pos[i]]<<" "<<l[fa[pos[i]]]+1<<endl; if(rt[pos[i]]==1){ qjchange(1,1,n,st[pos[i]]-mn+1,st[pos[i]],mn); ac::qjchange(1,1,n,max(1,st[pos[i]]-mn1+1),st[pos[i]]-mn,st[pos[i]]); } } for (int i=1;i<=n;i++) { int t=find(1,1,n,i); int t1=ac::find(1,1,n,i); if (t1!=inf) t=min(t,t1-i+1); if (t!=inf) printf("%d\n",t); else printf("0\n"); } }