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;
}

 

posted @ 2018-12-11 18:38  LiGuanlin  阅读(140)  评论(0编辑  收藏  举报