bzoj2780: [Spoj]8093 Sevenk Love Oimaster

建完sam以后自然而然的就是建后缀树了。

问题转化成多次询问一棵树(fail树)的子树中有多少不同颜色的节点。

等于一个序列的一段有多少种不同的颜色

这是个套路题,离线dfs序+树状数组可解(为什么我看了这么久愣是没看出来呢....)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

int aa[110000];
struct SAM
{
    int w[30],dep,fail,col;
}ch[210000];int cnt,las;
void insert(int dep,int col,int x)
{
    int pre=las,now=++cnt;
    ch[now].dep=dep,ch[now].col=col;
    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[nxt].fail=ch[now].fail=nnxt;
            while(pre!=0&&ch[pre].w[x]==nxt)
                ch[pre].w[x]=nnxt, pre=ch[pre].fail;
        }
    }
}

//--------------------------------------SAM---------------------------------------------------

struct node
{
    int x,y,next;
}a[210000];int len,last[210000];
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,L[210000],R[210000],co[210000];
void dfs(int x)
{
    L[x]=++z;co[z]=ch[x].col;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        dfs(y);
    }
    R[x]=z;
}
void MakeFailTree()
{
    len=0;memset(last,0,sizeof(last));
    for(int i=2;i<=cnt;i++)ins(ch[i].fail,i);
    z=0,dfs(1);
}

//-----------------------------------------init----------------------------------------------------

/*
    多次询问一棵树(fail树)的子树中有多少不同颜色的节点。
 == 一个序列的一段有多少种不同的颜色
*/
int s[210000];
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;
}

//-----------------------------------------calc----------------------------------------------------

char ss[110000];
struct query{int l,r,id;}q[61000];int qlen;
bool cmp(query q1,query q2){return q1.r<q2.r;} 
int ec[11000],as[61000];
int main()
{
    int n,Q,len;
    scanf("%d%d",&n,&Q);
    cnt=1;
    for(int t=1;t<=n;t++)
    {
        las=1;
        scanf("%s",ss+1);len=strlen(ss+1);
        for(int i=1;i<=len;i++)
            aa[i]=ss[i]-'a'+1, insert(i,t,aa[i]);
    }
    MakeFailTree();
    
    for(int t=1;t<=Q;t++)
    {
        scanf("%s",ss+1);len=strlen(ss+1);
        for(int i=1;i<=len;i++)aa[i]=ss[i]-'a'+1;
        
        int now=1; bool bk=true;
        for(int i=1;i<=len;i++)
        {
            int x=aa[i];
            if(ch[now].w[x]==0){bk=false;break;}
            else now=ch[now].w[x];
        }
        
        if(bk==false)as[t]=0;
        else q[++qlen].l=L[now],q[qlen].r=R[now],q[qlen].id=t;
    }
    
    sort(q+1,q+qlen+1,cmp); int tp=1;
    memset(ec,0,sizeof(ec));
    for(int i=1;i<=cnt;i++)
    {
        if(ec[co[i]]!=0)change(ec[co[i]],-1);
        ec[co[i]]=i,change(i,1);
        
        while(tp<=qlen&&q[tp].r==i)
        {
            as[q[tp].id]=getsum(q[tp].r)-getsum(q[tp].l-1);
            tp++;
        }
    }
    for(int i=1;i<=Q;i++)printf("%d\n",as[i]);
    
    return 0;
}

 

 


多次询问一棵树(fail树)的子树中有多少不同颜色的节点。 == 一个序列的一段有多少种不同的颜色

posted @ 2018-12-04 16:28  AKCqhzdy  阅读(268)  评论(0编辑  收藏  举报