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

 

posted @ 2018-12-23 18:57  AKCqhzdy  阅读(254)  评论(0编辑  收藏  举报