知识点 - 后缀自动机

知识点 - 后缀自动机

解决问题类型:

  1. 询问一些模式串P是否出现在文本串T中
  2. 询问一些模式串P文本串T中出现了几次
  3. 询问一些模式串P文本串T中第一次出现的位置
  4. 询问一些模式串P文本串T中所有出现的位置
  5. S串中有几个不同的子串
  6. S串中不同子串的总长度
  7. 求S字典序第K大的子串
  8. 求S字典序最小的循环位移(cyclic shift)
  9. 求S最短的没有出现的子串
  10. 求S1,S2的最长公共子串
  11. k k k个串 S i S_i Si的公共子串

论文:

2015 后缀自动机再字典树上的拓展

张天扬,《后缀自动机及其应用》,2015年国家集训队论文。

2014 陈立杰 后缀自动机

blog

定义与模板

直观上,某个串的SAM可以理解为其所有子串的高度压缩形式。时空复杂度为 O ( n ) O(n) O(n).

严格的定义是:可以接受串s所有后缀的最小的确定有穷自动机。

i.e.

  • SAM是一个有向图,转移是字符。如果我们从初始状态转移到一个终止状态,所有转移会拼出一个后缀。
struct state {
    int len, link;
    map<char, int> next;
};
const int MAXLEN = 100000;
state st[MAXLEN * 2];
int sz, last;
void sa_init() {
    st[0].len = 0;
    st[0].link = -1;
    sz++;
    last = 0;
}
void sa_extend(char c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    int p = last;
    while (p != -1 && !st[p].next.count(c)) {
        st[p].next[c] = cur;
        p = st[p].link;
    }
    if (p == -1) {
        st[cur].link = 0;
    } else {
        int q = st[p].next[c];
        if (st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        } else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].next = st[q].next;
            st[clone].link = st[q].link;
            while (p != -1 && st[p].next[c] == q) {
                st[p].next[c] = clone;
                p = st[p].link;
            }
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}

复杂度:

O ( n ) O(n) O(n) 时间 O ( n k ) O(n k) O(nk)空间

O ( n log ⁡ k ) O(n \log k) O(nlogk)时间 O ( n ) O(n) O(n)空间

k是字母表,用map存的话会多一个log

例题

求本质不同排名第k小的子串

exe:

串里长为n的字典序最小的子串

代码

#include<bits/stdc++.h>
using namespace std;
int n;
char s[1000001];
struct Suffixautomaton{
    struct edge{
        int to,next;
    }e[3000001];
    int cnt=1,last=1,g[2000001][27],pa[2000001],len[2000001],head[2000001],indo[2000001],sz[2000001],tot,id[2000001];
    inline void insert(int ch){
        int pos=last,newp=++cnt;
        last=newp;len[newp]=len[pos]+1;
        for (;pos&&!g[pos][ch];pos=pa[pos]) g[pos][ch]=newp;
        if (!pos) pa[newp]=1;
        else{
            int posx=g[pos][ch];
            if (len[pos]+1==len[posx]) pa[newp]=posx;
            else{
                int vs=++cnt;
                len[vs]=len[pos]+1;
                for (int i=0;i<26;i++) g[vs][i]=g[posx][i];
                pa[vs]=pa[posx];pa[posx]=pa[newp]=vs;
                for (;g[pos][ch]==posx;pos=pa[pos]) g[pos][ch]=vs;
            }
        } 
    }
    inline void bfs(){
        for (int i=1;i<=cnt;i++){indo[len[i]]++;}
        for (int i=1;i<=cnt;i++){indo[i]+=indo[i-1];}
        for (int i=1;i<=cnt;i++){id[indo[len[i]]--]=i;}
        for (int i=cnt;i>=1;i--){
            sz[id[i]]=1;
            for (int j=0;j<26;j++){
                int v=g[id[i]][j];
                if (!v) continue;
                sz[id[i]]+=sz[v];
            }
        }
    }
    inline void query(int k){
        int os=1;
        while (k){
            for (int i=0;i<26;i++){
                if (g[os][i]){
                    if (sz[g[os][i]]>=k){
                        putchar('a'+i);
                        os=g[os][i];
                        k--;break;
                    }else k-=sz[g[os][i]];
                }
            }
        }
        puts("");
    }
}sam;
int main(){
    scanf("%s",s);
    int L=strlen(s);
    for (int i=0;i<L;i++) sam.insert(s[i]-'a');
    sam.bfs();
    int Q;
    cin>>Q;
    while (Q--){
        int W;scanf("%d",&W);
        sam.query(W);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值