zoukankan      html  css  js  c++  java
  • bzoj3277: 串

    总的来讲,SAM难想,SA难写,SAM跑得快

    SAM:

    这题其实巨像bzoj2780

    建了广义SAM以后,用dfs序+树状数组求出每个点在多少不同的串里面

    然后枚举每个串在上面跑就行了。

    SA:

    然后如果要上后缀数组的话,把它们接在一起弄,中间插不同的乱七八糟的符号,先套路一手st表求LCP

    对于每个字符串枚举他的后缀,考虑每个后缀的前缀对答案的贡献,明显假如第2个前缀可以,第1个前缀也是可以的

    那我们可以二分这个前缀长度,找到满足LCP>=mid的前后最远位置ll、rr,然后去check是否包含了超过k种颜色

    check的方法:我们可以预处理出一个数组L,L[i]表示字典序排名第i的后缀,往前到L[i]才能够包含超过k种颜色,然后如果L[rr]>=ll就是可行的。

    这样的复杂度是nlog^2n的,但其实可以把二分前缀给省掉

    回忆SA height数组的求法,我们有height[i-1]<=height[i]-1,如果令第i个前缀的贡献为F[i],是否也满足F[i-1]<=F[i]-1呢?不难发现答案是肯定的,于是我们就可以用与求height数组类似的写法,达到nlogn的复杂度(虽然枚举不是严格的n。。。。)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    
    struct SAM
    {
        int w[30],dep,c,fail;
    }ch[410000];int cnt,las;
    void add(int dep,int x,int c)
    {
        int now=++cnt,pre=las;
        ch[now].dep=dep;ch[now].c=c;
        las=now;
        while(pre!=0&&ch[pre].w[x]==0)
            ch[pre].w[x]=now, pre=ch[pre].fail;
        if(pre==0)ch[now].fail=1;
        else
        {
            int nxt=ch[pre].w[x];
            if(ch[nxt].dep==ch[pre].dep+1)ch[now].fail=nxt;
            else
            {
                int nnxt=++cnt;
                ch[nnxt]=ch[nxt],ch[nnxt].dep=ch[pre].dep+1;
                
                ch[now].fail=ch[nxt].fail=nnxt;
                while(pre!=0&&ch[pre].w[x]==nxt)
                    ch[pre].w[x]=nnxt, pre=ch[pre].fail;
            }
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    struct query{int l,r,id;}q[410000];int qlen,RR;
    bool cmp(query q1,query q2){return q1.r==q2.r?q1.l<q2.l:q1.r<q2.r;}
    
    struct node
    {
        int x,y,next;
    }a[1100000];int len,last[410000];
    void ins(int x,int y)
    {
        len++;
        a[len].x=x;a[len].y=y;
        a[len].next=last[x];last[x]=len;
    }
    int z,ys[410000],cc[410000];
    void dfs(int x)
    {
        ys[x]=++z;cc[z]=ch[x].c;
        for(int k=last[x];k;k=a[k].next)dfs(a[k].y);
        
        qlen++;
        q[qlen].l=ys[x],q[qlen].r=z,q[qlen].id=x;
        RR=max(RR,q[qlen].r);
    }
    void MakeFailTree()
    {
        for(int i=2;i<=cnt;i++)ins(ch[i].fail,i);
        dfs(1);
    }
    
    //----------------------------------------------- SAM && make fail tree---------------------------------------------------------------
    
    int s[410000];
    int lowbit(int x){return x&-x;}
    void change(int x,int k){ while(x<=cnt+10){s[x]+=k;x+=lowbit(x);} }
    int getsum(int x){ int ret=0; while(x>0){ret+=s[x];x-=lowbit(x);} return ret; }
    //.............bit.................
    int cla[410000],num[410000];
    void calc()//计算:每棵子树中的颜色数==当前节点管理的子串能够被多少串表示
    {
        sort(q+1,q+qlen+1,cmp);
        int tp=1;
        for(int i=1;i<=RR;i++)
        {
            if(cla[cc[i]]!=0) change(cla[cc[i]],-1);
            cla[cc[i]]=i; change(cla[cc[i]],1);
            
            while(tp<=qlen&&q[tp].r==i)
            {
                num[q[tp].id]=getsum(q[tp].r)-getsum(q[tp].l-1);
                tp++;
            }
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    int K,b[410000],sum[410000];//当前位置是否满足条件,我的前缀有多少满足条件(不包括自己) 
    void godfs(int x)
    {
        if(num[x]>=K)b[x]=1;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            sum[y]=sum[x]+b[x]*(ch[x].dep-ch[ch[x].fail].dep);
            godfs(y);
        }
    }
    
    //---------------------------------------------------------------------------------------------------------------------------------- 
    
    vector<int>vec[110000];
    int work(int o)
    {
        int now=1,L=0,ans=0;
        for(int i=0;i<vec[o].size();i++)
        {
            int x=vec[o][i];
            while(now!=0&&ch[now].w[x]==0)
                now=ch[now].fail, L=ch[now].dep;
            if(now==0)
                now=1, L=0;
            else
            {
                now=ch[now].w[x],L++;
                ans+=sum[now]+b[now]*(L-ch[ch[now].fail].dep);
            }
        }
        return ans;
    }
    
    //---------------------------------------------------------------------------------------------------------------------------------
    
    char ss[110000];int sslen;
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        int n;
        scanf("%d%d",&n,&K); cnt=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ss+1),sslen=strlen(ss+1);
            las=1;
            for(int j=1;j<=sslen;j++)
            {
                add(j,ss[j]-'a'+1,i),vec[i].push_back(ss[j]-'a'+1);
            }
        }
        z=qlen=RR=0;
        MakeFailTree(),calc();
        godfs(1);
        
        for(int i=1;i<=n;i++)
            printf("%d ",work(i));
        
        return 0;
    }
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    int ss[210000];
    int sa1[210000],Rank[210000];
    int sa2[210000],Rsort[210000],tt[210000];
    void get_sa(int n,int m)
    {
        for(int i=1;i<=n;i++)Rank[i]=ss[i];
        
        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=n;i++)Rsort[Rank[i]]++;
        for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
        for(int i=n;i>=1;i--)sa1[Rsort[Rank[i]]--]=i;
        
        int ln=1,p=0;
        while(p<n)
        {
            int k=0;for(int i=n-ln+1;i<=n;i++)sa2[++k]=i;
            for(int i=1;i<=n;i++)
                if(sa1[i]-ln>0)sa2[++k]=sa1[i]-ln;
        
            memset(Rsort,0,sizeof(Rsort));
            for(int i=1;i<=n;i++)Rsort[Rank[sa2[i]]]++;
            for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
            for(int i=n;i>=1;i--)sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
            
            memcpy(tt,Rank,sizeof(tt));
            p=1;Rank[sa1[1]]=p;
            for(int i=2;i<=n;i++)
            {
                if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])p++;
                Rank[sa1[i]]=p;
            }
            ln*=2;m=p;
        }
    }
    int height[210000];
    void get_he(int n)
    {
        int h=0;
        for(int i=1;i<=n;i++)
        {
            int j=sa1[Rank[i]-1];
            if(h!=0)h--;
            while(ss[i+h]==ss[j+h])h++;
            height[Rank[i]]=h;
        }
    }
    
    //--------------------------------------------------sa-------------------------------------------------------------
    
    int Bin[30],Log[210000];
    int f[30][210000],d[210000];
    void get_st(int n)
    {
        Bin[0]=1;for(int i=1;i<=25;i++)Bin[i]=Bin[i-1]*2;
        Log[1]=0;for(int i=2;i<=n ;i++)Log[i]=Log[i/2]+1;
        
        memset(f,63,sizeof(f));
        for(int i=0;i<=n;i++)f[0][i]=height[i];
        for(int j=1;j<=25;j++)
            for(int i=1;i+Bin[j]-1<=n;i++)
                f[j][i]=min(f[j-1][i],f[j-1][i+Bin[j-1]]); 
    }
    int getLCP(int l,int r)
    {
        l++;if(l>r)return (1<<30);
        int k=Log[r-l+1];
        return min(f[k][l],f[k][r-Bin[k]+1]);
    }
    
    //------------------------------------------------套路st表---------------------------------------------------------
    
    int K,L[210000],b[210000],c[210000];
    void getL(int N,int n)
    {
        int l=N+1,s=0;
        for(int r=N+1;r<=n;r++)
        {
            c[b[sa1[r]]]++;
            if(c[b[sa1[r]]]==1)s++;
            
            bool flag=false;
            while(l<r&&s>=K)
            {
                flag=true;
                c[b[sa1[l]]]--;
                if(c[b[sa1[l]]]==0)s--;
                l++;
            }
            if(flag)
            {
                l--;
                c[b[sa1[l]]]++;
                if(c[b[sa1[l]]]==1)s++;
            }
            
            if(s>=K)L[r]=l;
        }
    }
    
    //----------------------------------------------------------------------------------------------------------
    
    bool check(int x,int p,int n)
    {
        int l,r,ll,rr;
        
        l=1,r=x-1,ll=x;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(getLCP(mid,x)>=p)
            {
                ll=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        
        l=x+1,r=n,rr=x;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(getLCP(x,mid)>=p)
            {
                rr=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        
        if(ll<=L[rr])return true;
        else return false;
    }
    int OL[210000],OR[210000];
    int solve(int o,int n)
    {
        int p=0,ans=0;
        for(int i=OL[o];i<=OR[o];i++)
        {
            if(p!=0)p--;
            while(check(Rank[i],p,n)==true)p++;
            p--;
            ans+=p;
        }
        return ans;
    }
    
    char sc[210000];
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        int n;
        scanf("%d%d",&n,&K);
        int sslen=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",sc+1);int sclen=strlen(sc+1);
            OL[i]=sslen+1;
            for(int j=1;j<=sclen;j++)
                ss[++sslen]=n+sc[j]-'a'+1,b[sslen]=i,d[sslen]=sclen-j+1;
            OR[i]=sslen;
            ss[++sslen]=i;
        }
        get_sa(sslen,120000),get_he(sslen);
        get_st(sslen);
        getL(n,sslen);
        for(int i=1;i<=n;i++);
        for(int i=1;i<=n;i++)
            printf("%d ",solve(i,sslen));
        
        return 0;
    }
  • 相关阅读:
    TensorFlow实现线性回归
    在window10平台下安装TensorFlow(only cpu)
    毕设——基于深度学习的恶意代码检测
    play ball小游戏-Alpha版本发布说明
    支持3000万道四则运算题目生成的小学生毁灭姬
    图片集
    个人项目WordCount(C++/QT)
    自我介绍+软工5问
    大数据应用技术课程实践--选题与实践方案
    15 手写数字识别-小数据集
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/10165173.html
Copyright © 2011-2022 走看看