zoukankan      html  css  js  c++  java
  • 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
    ",min(a,b));
        }
        return 0;
    }
  • 相关阅读:
    PHP获取http头信息
    zend studio 改变背景颜色
    WebApp之Meta标签
    js的左右滑动触屏事件
    越狱的 iPhone、iPad 通过网站实现一键安装 ipa 格式的 APP 应用
    移动平台的meta标签神奇的功效
    HTML5 localStorage本地存储实际应用举例
    移动端开发小结
    openSUSE 安装源
    两种方法求Int最值
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8986249.html
Copyright © 2011-2022 走看看