jzoj4025-找回密码【后缀自动机】

正题


大意

一个字符串,要求第k小的子串。


解题思路

先建立一个后缀自动机,然后用一个 n u m i 表示第 i 个节点的可以到达的点所表示的子串总和,然后从第1号点开始查找,判断一下找到第k小所在的节点后,然后查找输出。


代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200010
#define Get(W) W>='a'?W-71:W-'A'
#define fuGet(W) W>=26?W+71:W+'A'
using namespace std;
int l,last,fail[N],next[N][56],tot,len[N],num[N];
int ls[N],v[N],head,tail,q[N];
long long sum;
char s[100001];
int New(int x,int y){
    next[x][y]=++tot;
    len[tot]=len[x]+1;
}
void bfs()
{
    head=0;
    q[1]=1;tail=1;v[1]=1;
    do
    {
        int x=q[++head];
        for (int i=0;i<52;i++)
          if (next[x][i]&&!v[next[x][i]])
          {
            v[next[x][i]]=1;
            q[++tail]=next[x][i];//建立顺序
          }
    }
    while (head<tail);
    for (;tail;tail--)
    {
        int x=q[tail];
        for (int i=0;i<52;i++)
          if (next[x][i])
          {
            ls[x]=i;
            num[x]+=num[next[x][i]]+1;//按顺序计算答案
          }
    }
}
void Get_answer()
{
    int x=1;
    while (sum)
    {
        if (!ls[x]&&!next[x][0])break;
        for (int i=0;i<=ls[x];i++)
        if (next[x][i])
        {
          if (num[next[x][i]]+1>=sum||i==ls[x])//在当前节点的搜索树中
          {
            printf("%c",fuGet(i));
            int y=next[x][i];
            sum--;
            if (sum) x=y;//进入该节点
            break;
          }
          else sum-=num[next[x][i]]+1;//跳转下一棵
        }
    }
}
int main()
{
    scanf("%s",s);
    scanf("%lld",&sum);
    l=strlen(s);
    last=1;
    len[1]=0;fail[1]=0;tot=1;
    for (int i=0;i<l;i++)
    {
        int addc=Get(s[i]);
        int k=last,j=0;
        New(last,addc);//新建节点
        last=tot;
        for (j=fail[k];j;j=fail[j])
          if (!next[j][addc])
            next[j][addc]=last;//直接连接
          else
          {
            int z=next[j][addc];
            if (len[z]==len[j]+1)
              fail[last]=z;//连接
            else
            {
              New(j,addc);
              for (int l=0;l<52;l++)
                next[tot][l]=next[z][l];//继承指针
              fail[tot]=fail[z];fail[z]=fail[last]=tot;//继承
              for (int l=j;l;l=fail[l])
                if (next[l][addc]==z) next[l][addc]=tot;//继承连接
            }
            break;
          }
        if (!j) fail[last]=1;/特判
    }
    bfs();//计算num
    Get_answer();
}

猜你喜欢

转载自blog.csdn.net/mr_wuyongcong/article/details/81062937