spoj7258:Lexicographical Substring Search 后缀自动机

来源:互联网 发布:微信广告植入系统源码 编辑:程序博客网 时间:2024/05/19 04:28

经典题,找不重复字典序第k小的字符串。
先跑出SAM,然后在SAM上DP。
因为从SAM的起点跑可以跑出所有的子串,所以我们用dpi表示从i点出发跑出的子串的个数,显然有

dpi=j=azdpchi,j+1

然后每次按照a..z的顺序从起点开始dfs,如果沿chi,j的方向走下去的个数k(即dpchi,jk),就沿着这个方向往下走,否则令k=kdpchi,j,继续dfs

如果求重复字典序第k小的话,我们可以在加入的时候给np记一下数,最后从起点到点i的串出现的次数就是iparent树中子树的计数之和。(其实就是求出right集合的大小)

#include<iostream>#include<cstdio>#include<cstring>#define N 200005using namespace std;char s[N];int last,S,n,cnt,Q;int fa[N],len[N],ch[N][26],f[N],cc[N],q[N];inline int read(){    int a=0,f=1; char c=getchar();    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}    return a*f;}inline void insert(int c){    int p=last,np=last=++cnt;    len[np]=len[p]+1;    while (p&&!ch[p][c]) ch[p][c]=np,p=fa[p];    if (!p) fa[np]=S;    else    {        int q=ch[p][c];        if (len[q]==len[p]+1) fa[np]=q;        else        {            int nq=++cnt;            memcpy(ch[nq],ch[q],sizeof(ch[q]));            len[nq]=len[p]+1;            fa[nq]=fa[q]; fa[q]=fa[np]=nq;            while (p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];        }    }}void query(){    int p=1,x=read();    while (x)    {        for (int i=0;i<26;i++)            if (ch[p][i])            {                if (f[ch[p][i]]>=x)                {                    putchar('a'+i);                    p=ch[p][i];                    x--;                    break;                }                else x-=f[ch[p][i]];            }    }    puts("");}int main(){    last=S=++cnt;    scanf("%s",s+1);    n=strlen(s+1);    for (int i=1;i<=n;i++)        insert(s[i]-'a');    for (int i=1;i<=cnt;i++) ++cc[len[i]];    for (int i=1;i<=cnt;i++) cc[i]+=cc[i-1];    for (int i=1;i<=cnt;i++) q[cc[len[i]]--]=i;    for (int i=cnt;i;i--)    {        f[q[i]]=1;        for (int j=0;j<26;j++)            f[q[i]]+=f[ch[q[i]][j]];    }    Q=read();    while (Q--) query();    return 0;}
0 0
原创粉丝点击