1. 程式人生 > >bzoj 1396: 識別子串 (字尾自動機+線段樹)

bzoj 1396: 識別子串 (字尾自動機+線段樹)

1396: 識別子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 308  Solved: 190
[Submit][Status][Discuss]

Description

Input

一行,一個由小寫字母組成的字串S,長度不超過10^5

Output

L行,每行一個整數,第i行的資料表示關於S的第i個元素的最短識別子串有多長.

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
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");
	}
}