SAM(后缀自动机)模板

39 篇文章 6 订阅
21 篇文章 0 订阅

个人感觉这个SAM比SA要难理解的多,两者好像功能有相似之处,不过SAM在一些题目上有它的优势
把板子和模板题记录下来,方便以后抄标

学习小记


模板题:【GDOI2012】字符串

Description

mmm正在学习字典序。现在老师给她布置了一个作业:给出一个字符串,问该字符串的所有不同的子串中,按字典序排第K的字串。由于众所周知的原因,mmm需要你为她解决这个问题。

Input

第1行输入一个只有小写字母组成的字符串。
第2行输入N,为询问个数。
第3到N+2行每行输入一个整数Ki。

Output

输出N行,第i行输出按字典序排第Ki的子串,如果Ki超出了子串数量,输出-1.

Sample Input

abbb
8
1
2
3
4
5
6
7
8

Sample Output

a
ab
abb
abbb
b
bb
bbb
-1

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 20100
using namespace std;
char s[N];
int n,last=1,tot=1,a[N],b[N*10],q,sum[N*10];
struct SAM{
    int len,size,fail;
    int to[26];
}t[N*10];
void add(int x)
{
    int p=last,np=++tot;
    t[np].len=t[p].len+1;t[np].size=1;last=np;
    for(;p&&t[p].to[x]==0;p=t[p].fail) t[p].to[x]=np;
    if(p==0){t[np].fail=1;return;}
    int q=t[p].to[x];
    if(t[p].len+1==t[q].len) t[np].fail=q;
    else
    {
        int nq=++tot;t[nq]=t[q];
        t[nq].len=t[p].len+1;t[q].fail=t[np].fail=nq;
        for(;p&&t[p].to[x]==q;p=t[p].fail) t[p].to[x]=nq;
    }
}
void ssort()
{
    fo(i,1,tot) a[t[i].len]++;
    fo(i,1,n) a[i]+=a[i-1];
    fd(i,tot,1) b[a[t[i].len]--]=i;
}
void bfs(int k)
{
    int x=1;
    while(k>t[x].size)
    {
        k-=t[x].size;
        fo(i,0,25)
        if(t[x].to[i]>0)
        {
            if(sum[t[x].to[i]]>=k)
            {
                x=t[x].to[i];
                putchar(i+97);
                break;
            }
            k-=sum[t[x].to[i]];
        }   
    }
    putchar('\n');
}
int main()
{
    scanf("%s\n",s+1);n=strlen(s+1);
    fo(i,1,n) add(s[i]-97);
    ssort();
    fd(i,tot,1)
    {
        int x=b[i];sum[x]=t[x].size;
        fo(i,0,25) sum[x]+=sum[t[x].to[i]];
    }
    scanf("%d",&q);
    while(q--)
    {
        int k;scanf("%d",&k);
        if(k>sum[1]) {printf("-1\n");continue;}
        bfs(k);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值