BZOJ 4327: JSOI2012 玄武密码

后缀自动机裸题。
借着这道裸题总结一下后缀自动机的查询问题。
1.查前缀 查询时不跳parent,遇到空节点就跳出。
2.查子串 查询时跳parent,记录最大ans.
3.查次数 LCT维护right数组
4.查不同的串的数目 在建树时维护,一个点对答案的贡献为this->max_len - this->parent->max_len

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct sam
{
    sam* son[4];
    sam* parent;
    int max_len;
    sam(int _=0):max_len(_),parent(0x0)
    {
        memset(son,0,sizeof(son));
    }
}*root=new sam(),*last=root;
void my_insert(int x)
{
    sam *p=last;
    sam *np=new sam(p->max_len+1);
    while(p && !p->son[x])
    {
        p->son[x]=np;
        p=p->parent;
    }
    if(!p) np->parent=root;
    else
    {
        sam *q=p->son[x];
        if(q->max_len==p->max_len+1) np->parent=q;
        else
        {
            sam *nq=new sam(p->max_len+1);
            nq->parent=q->parent;
            memcpy(nq->son,q->son,sizeof(nq->son));
            q->parent=nq,np->parent=nq;
            while(p && p->son[x]==q)
            {
                p->son[x]=nq;
                p=p->parent;
            }
        }
    }
    last=np;
}
char s[10000000];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='E') my_insert(0);
        if(s[i]=='S') my_insert(1);
        if(s[i]=='W') my_insert(2);
        if(s[i]=='N') my_insert(3);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s+1);
        sam *o=root;
        int ans=0;
        for(int j=1;s[j];j++)
        {
            int x;
            if(s[j]=='E') x=0;
            if(s[j]=='S') x=1;
            if(s[j]=='W') x=2;
            if(s[j]=='N') x=3;
            if(o->son[x]) o=o->son[x],ans++;
            else break;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(BZOJ,后缀自动机)