您的位置:首页 > 其它

bzoj 1396: 识别子串 (后缀自动机+线段树)

2017-01-12 21:33 423 查看

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



[Submit][Status][Discuss]

题解:后缀自动机+线段树

这道题可以用来更新答案的状态一定的是|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
,ch
[30],l
,pos
,rt
,v
,n,inf;
int p,np,nq,q,last,root,cnt,delta[N*4],tr[N*4],st
;
char s
;
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");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: