后缀自动机刷题小结

鉴于被最近CLJ大神出的多校赛虐的体无完肤后,决心怒学神奇的后缀自动机。

第一遍看CLJ大神的ppt后完全就是晕头转向。

然后我看了一个OIer的blog,算是大概明白了后缀自动机的构造过程,理解了之后发现代码还是比较容易写的,基本不需要模板。

之后就是刷题了……可是以刷题就发现各种不会做……仲么破!!!

各种借鉴之后,才把几道算是模板的题都刷完了,还需要再深入理解啊,关于性质的证明完全木有理解……

SPOJ NSUBSTR(借鉴别人的写法用了桶排的方法更新right值,保证了O(n)的复杂度)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=600010;
const int sigma=26;
char s[maxn];
int sz;
struct state
{
    state *pre,*go[sigma];
    int val,v;
    void clear()
    {
        pre=0;val=0;
        memset(go,0,sizeof(go));
    }
};
state *root,*last;
state st[maxn],*r[maxn];
int t[maxn],ans[maxn];
void init()
{
    sz=0;
    root=last=&st[sz++];
    root->clear();
}
void extend(int w)
{
    state *p=last;
    state *np=&st[sz++];
    np->clear();
    np->val=p->val+1;
    while(p && p->go[w]==0)
    {
        p->go[w]=np;
        p=p->pre;
    }
    if(p==0) np->pre=root;
    else
    {
        state *q=p->go[w];
        if(q->val==p->val+1)
            np->pre=q;
        else
        {
            state *nq=&st[sz++];
            nq->clear();
            nq->val=p->val+1;
            memcpy(nq->go,q->go,sizeof(q->go));
            nq->pre=q->pre;
            q->pre=nq;
            np->pre=nq;
            while(p && p->go[w]==q)
            {
                p->go[w]=nq;
                p=p->pre;
            }
        }
    }
    last=np;
}
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        int n=strlen(s);
        init();
        for(int i=0;i<n;i++) extend(s[i]-'a');
        for(int i=0;i<=n;i++) t[i]=0,ans[i]=0;
        for(int i=1;i<sz;i++) t[st[i].val]++;
        for(int i=1;i<=n;i++) t[i]+=t[i-1];
        for(int i=1;i<sz;i++) r[t[st[i].val]--]=&st[i];
        state *now=root;
        for(int i=0;i<n;i++) (now=now->go[s[i]-'a'])->v=1;
        for(int i=sz-1;i>0;i--)
        {
            ans[r[i]->val]=max(ans[r[i]->val],r[i]->v);
            r[i]->pre->v+=r[i]->v;
        }
        for(int i=n-1;i>=1;i--) ans[i]=max(ans[i],ans[i+1]);
        for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    }
    return 0;
}

SPOJ SUBLEX(这道题卡时间卡的很紧,还必须将后缀自动机的边离散化才行)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=200000;
const int sigma=26;
struct state
{
    state *pre,*go[sigma],*d[sigma+1];
    char str[sigma];
    int val,size,sum;
    void clear()
    {
        pre=0;val=0;
        memset(go,0,sizeof(go));
    }
} st[maxn],*root,*last,*r[maxn];
int t[maxn],sz;
void init()
{
    sz=0;
    root=last=&st[sz++];
    root->clear();
}
void extend(int w)
{
    state *p=last;
    state *np=&st[sz++];
    np->clear();
    np->val=p->val+1;np->size=1;
    while(p && p->go[w]==0)
    {
        p->go[w]=np;
        p=p->pre;
    }
    if(p==0) np->pre=root;
    else
    {
        state *q=p->go[w];
        if(q->val==p->val+1)
            np->pre=q;
        else
        {
            state *nq=&st[sz++];
            nq->clear();
            nq->val=p->val+1;nq->size=1;
            memcpy(nq->go,q->go,sizeof(q->go));
            nq->pre=q->pre;
            q->pre=nq;
            np->pre=nq;
            while(p && p->go[w]==q)
            {
                p->go[w]=nq;
                p=p->pre;
            }
        }
    }
    last=np;
}
void add(state *k,int j,int cnt)
{
    k->size+=k->go[j]->size;
    k->d[cnt]=k->go[j];
    k->str[cnt]='a'+j;
}
void dfs(state *p,int k)
{
    if(p->sum==0 || k==0) return;
    int tmp=0;
    for(int i=1;i<=p->sum;i++)
    {
        tmp=i;
        if(k>p->d[i]->size)
        {
            k-=p->d[i]->size;
        }
        else
        {
            k--;
            break;
        }
    }
    printf("%c",p->str[tmp]);
    dfs(p->d[tmp],k);
}
char s[maxn];
int q,n,l;
int main()
{
    scanf("%s",s);
    n=strlen(s);
    init();
    for(int i=0;i<n;i++) extend(s[i]-'a');
    for(int i=0;i<=n;i++) t[i]=0;
    for(int i=1;i<sz;i++) t[st[i].val]++;
    for(int i=1;i<=n;i++) t[i]+=t[i-1];
    for(int i=0;i<sz;i++) r[t[st[i].val]--]=&st[i];
    for(int i=sz-1;i>=0;i--)
    {
        r[i]->sum=0;
        for(int j=0;j<sigma;j++)
            if(r[i]->go[j])
                add(r[i],j,++(r[i]->sum));
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d",&l);
        dfs(root,l);
        puts("");
    }
    return 0;
}
SPOJ LCS(SPOJ把这题的C++禁了,只能改成C……)

#include <stdio.h>
#include <string.h>
#define maxn 600000
#define sigma 26
struct state
{
    struct state *pre,*go[sigma];
    int val;
} *root,*last,st[maxn];
int sz;
void init()
{
    sz=0;
    root=last=&st[sz++];
    root->val=0;root->pre=0;
    memset(root->go,0,sizeof(root->go));
}
void extend(int w)
{
    struct state *p=last;
    struct state *np=&st[sz++];
    memset(np->go,0,sizeof(np->go));
    np->val=p->val+1;
    while(p && p->go[w]==0)
    {
        p->go[w]=np;
        p=p->pre;
    }
    if(p==0) np->pre=root;
    else
    {
        struct state *q=p->go[w];
        if(q->val==p->val+1)
            np->pre=q;
        else
        {
            struct state *nq=&st[sz++];
            nq->val=p->val+1;
            memcpy(nq->go,q->go,sizeof(q->go));
            nq->pre=q->pre;
            q->pre=nq;np->pre=nq;
            while(p && p->go[w]==q)
            {
                p->go[w]=nq;
                p=p->pre;
            }
        }
    }
    last=np;
}
char s1[maxn],s2[maxn];
int main()
{
    scanf("%s%s",s1,s2);
    int n=strlen(s1),m=strlen(s2);
    init();
    int i;
    for(i=0;i<n;i++) extend(s1[i]-'a');
    int tmp=0,ans=0;
    struct state* p=root;
    for(i=0;i<m;i++)
    {
        if(p->go[s2[i]-'a'])
        {
            p=p->go[s2[i]-'a'];
            tmp++;
        }
        else
        {
            while(p && p->go[s2[i]-'a']==0) p=p->pre;
            if(p)
            {
                tmp=p->val+1;
                p=p->go[s2[i]-'a'];
            }
            else
            {
                tmp=0;
                p=root;
            }
        }
        if(tmp>ans) ans=tmp;
    }
    printf("%d\n",ans);
    return 0;
}

hdu 4622(CLJ大神的题,用后缀自动机可以很容易的做出来)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2010;
const int maxq=10010;
const int sigma=26;
char s[maxn];
int q,sz;
struct state
{
    state *pre,*go[sigma];
    int val;
    void clear()
    {
        pre=0;val=0;
        memset(go,0,sizeof(go));
    }
};
state *root,*last;
state st[10000];
void init()
{
    sz=0;
    root=last=&st[sz++];
    root->clear();
}
void extend(int w)
{
    state *p=last;
    state *np=&st[sz++];
    np->clear();
    np->val=p->val+1;
    while(p && p->go[w]==0)
        p->go[w]=np,p=p->pre;
    if(p==0)
        np->pre=root;
    else
    {
        state *q=p->go[w];
        if(p->val+1==q->val)
            np->pre=q;
        else
        {
            state *nq=&st[sz++];
            nq->clear();
            nq->val=p->val+1;
            memcpy(nq->go,q->go,sizeof(q->go));
            nq->pre=q->pre;
            q->pre=nq;
            np->pre=nq;
            while(p && p->go[w]==q)
                p->go[w]=nq,p=p->pre;
        }
    }
    last=np;
}
struct query
{
    int s,e,no;
    query(int s=0,int e=0,int no=0):s(s),e(e),no(no){}
    bool operator<(const query& tmp) const
    {
        return s<tmp.s || (s==tmp.s && e<tmp.e);
    }
} a[maxq];
int ans[maxq];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s);
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            int s,e;
            scanf("%d%d",&s,&e);
            a[i]=query(s,e,i);
        }
        sort(a,a+q);
        for(int i=0;i<q;i++)
        {
            if(i!=0 && a[i].s==a[i-1].s)
            {
                if(a[i].e==a[i-1].e)
                    ans[a[i].no]=ans[a[i-1].no];
                else
                {
                    for(int j=a[i-1].e+1;j<=a[i].e;j++)
                        extend(s[j-1]-'a');
                    ans[a[i].no]=0;
                    for(int j=sz-1;j>0;j--) ans[a[i].no]+=st[j].val-st[j].pre->val;
                }
            }
            else
            {
                init();
                for(int j=a[i].s;j<=a[i].e;j++)
                    extend(s[j-1]-'a');
                ans[a[i].no]=0;
                for(int j=sz-1;j>0;j--) ans[a[i].no]+=st[j].val-st[j].pre->val;
            }
        }
        for(int i=0;i<q;i++) printf("%d\n",ans[i]);
    }
    return 0;
}

hdu 4641(多校第四场的一道题,正解不是后缀自动机,但可以通过暴力回溯更新right值水过)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int sigma=26;
const int maxn=600000;
struct state
{
    state *pre,*go[sigma];
    int val,cnt,flag;
    void clear()
    {
        pre=0;val=0;cnt=1;flag=0;
        memset(go,0,sizeof(go));
    }
} st[maxn],*root,*last;
int sz,n,m,k;
long long ans;
void init()
{
    sz=0;
    root=last=&st[sz++];
    root->clear();
    root->cnt=0;
}
void extend(int w)
{
    state *p=last;
    state *np=&st[sz++];
    np->clear();
    np->val=p->val+1;
    if(k==1)
    {
        np->flag=1;
        ans++;
    }
    while(p&&p->go[w]==0)
    {
        p->go[w]=np;
        p=p->pre;
    }
    if(p==0) np->pre=root;
    else
    {
        state *q=p->go[w];
        if(q->val==p->val+1)
        {
            np->pre=q;
            state *tmp=q;
            while(tmp!=root)
            {
                if(tmp->flag==1) break;
                (tmp->cnt)++;
                if(tmp->cnt>=k&&tmp->flag==0)
                {
                    tmp->flag=1;
                    ans+=tmp->val-tmp->pre->val;
                }
                tmp=tmp->pre;
            }
        }
        else
        {
            state *nq=&st[sz++];
            *nq=*q;
            nq->val=p->val+1;
            q->pre=nq;
            np->pre=nq;
            while(p&&p->go[w]==q)
            {
                p->go[w]=nq;
                p=p->pre;
            }
            state *tmp=nq;
            while(tmp!=root)
            {
                if(tmp->flag==1) break;
                (tmp->cnt)++;
                if(tmp->cnt>=k&&tmp->flag==0)
                {
                    tmp->flag=1;
                    ans+=tmp->val-tmp->pre->val;
                }
                tmp=tmp->pre;
            }
        }
    }
    last=np;
}
char s[maxn],c[2];
int o;
int main()
{
    while(scanf("%d%d%d",&n,&m,&k)==3)
    {
        scanf("%s",s);
        init();ans=0;
        for(int i=0;i<n;i++) extend(s[i]-'a');
        while(m--)
        {
            scanf("%d",&o);
            if(o==1)
            {
                scanf("%s",c);
                extend(c[0]-'a');
            }
            else
            {
                printf("%I64d\n",ans);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值