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

https://www.lydsy.com/JudgeOnline/problem.php?id=1396

 

后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点

而一个状态所代表子串的出现次数就是子树中叶子节点的个数

所以只有叶子节点 即 |Right|=1的状态 代表的子串 出现了1次

我们计算以每一个位置为子串右端点时,它对一些位置的贡献

枚举|Right|=1的状态s

令end=Right(s)

那么以end为子串右端点,长度在[1,Max(parent(s))]的子串至少还会在s的父节点表示的状态中出现

所以在以end为识别子串右端点时

位置[end-Max(parent(s)),end]的最短长度为Max(parent(s)+1

位置[end-Max(s)+1,end-Max(parent(s))]的最短长度为 end-i+1

用两棵线段树维护

一棵直接维护最小值

另一棵维护end+1的最小值,查询的时候将结果-i

 

#include<cstdio>
#include<cstring>
#include<algorithm>
 
#define N 100001
 
using namespace std;
 
char s[N];
 
int ch[N<<1][26],tot=1;
int fa[N<<1],len[N<<1];
int siz[N<<1]; 
int last=1,p,q,np,nq;
 
int leaf[N];
 
int v[N],sa[N<<1];
 
struct Segment
{
    int mx[N<<2];
    int tag[N<<2];
 
    void down(int k)
    {
        mx[k<<1]=min(mx[k<<1],tag[k]);
        mx[k<<1|1]=min(mx[k<<1|1],tag[k]);
        tag[k<<1]=min(tag[k<<1],tag[k]);
        tag[k<<1|1]=min(tag[k<<1|1],tag[k]);
        tag[k]=2e9;
    }
 
    void build(int k,int l,int r)
    {
        mx[k]=tag[k]=2e9;
        if(l==r) return;
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
 
    void change(int k,int l,int r,int opl,int opr,int w)
    {
        if(l>=opl && r<=opr)
        {
            mx[k]=min(mx[k],w);
            tag[k]=min(tag[k],w);
            return;
        }
        int mid=l+r>>1;
        if(tag[k]!=2e9) down(k);
        if(opl<=mid) change(k<<1,l,mid,opl,opr,w);
        if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,w);
        mx[k]=min(mx[k<<1],mx[k<<1|1]);
    }
 
    int query(int k,int l,int r,int x)
    {
        if(l==r) return mx[k];
        int mid=l+r>>1;
        if(tag[k]!=2e9) down(k);
        if(x<=mid) return query(k<<1,l,mid,x);
        return query(k<<1|1,mid+1,r,x);
    }
};
 
Segment tr1,tr2;
 
void extend(int c)
{
    len[np=++tot]=len[last]+1;
    siz[tot]=1;
    for(p=last;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    if(!p) fa[np]=1;
    else
    {
        q=ch[p][c];
        if(len[q]==len[p]+1) fa[np]=q;
        else
        {
            nq=++tot;
            fa[nq]=fa[q];
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[q]=fa[np]=nq;
            len[nq]=len[p]+1;
            for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
        }
    }
    last=np;
}
     
int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;++i) 
    {
        leaf[i]=tot+1;
        extend(s[i]-'a');
    }
    for(int i=1;i<=tot;++i) v[len[i]]++;
    for(int i=1;i<=n;++i) v[i]+=v[i-1];
    for(int i=1;i<=tot;++i) sa[v[len[i]]--]=i;
    int x;
    for(int i=tot;i;--i)
    {
        x=sa[i];
        siz[fa[x]]+=siz[x];
    }
    tr1.build(1,1,n);
    tr2.build(1,1,n);
    int l,r,end;
    for(int x=2;x<=tot;++x)
    if(siz[x]==1)
    {
        l=len[fa[x]];
        r=len[x];
        end=len[x];
        tr1.change(1,1,n,end-l,end,l+1);
        tr2.change(1,1,n,end-r+1,end-l,end+1);
    }
    int a,b;
    for(int i=1;i<=n;++i)
    {
        a=tr1.query(1,1,n,i);
        b=tr2.query(1,1,n,i)-i;
        printf("%d\n",min(a,b));
    }
    return 0;
}
posted @ 2018-05-03 16:52  TRTTG  阅读(304)  评论(0编辑  收藏  举报