后缀自动机(SAM) 合集

先上模板

int len[maxn << 1],fa[maxn << 1],son[maxn << 1][maxc];
LL num[maxn << 1];
int size,last;
void Init(){
    size = last = 1;
}
void insert(char c){
    int s = c - ‘a‘;
    int p = last,np = ++size;last = np; num[np] = 1; //主链结点出现次数 + 1
    len[np] = len[p] + 1;
    for(;p && !son[p][s]; p = fa[p]) son[p][s] = np;
    if(!p) fa[np] = 1;
    else{
        int q = son[p][s];
        if(len[p] + 1 == len[q]) fa[np] = q;
        else{
            int nq = ++size; len[nq] = len[p] + 1;
            memcpy(son[nq],son[q],sizeof(son[q]));
            fa[nq] = fa[q]; fa[q] = fa[np] = nq;
            for(;son[p][s] == q && p;p = fa[p]) son[p][s] = nq;
        }
    }
}
void insert(char *s){
    Init();
    for(int i = 0; s[i] ; i ++) insert(s[i]);
}

9.10 update

P3804 【模板】后缀自动机

dfs parent树可以统计每个子串出现的次数

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<‘0‘ || c>‘9‘){if (c == ‘-‘) f = -1;c = getchar();}
while (c >= ‘0‘&&c <= ‘9‘){x = x * 10 + c - ‘0‘;c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 1e6 + 10;
const int maxc = 26;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int N,M,K;
int len[maxn << 1],fa[maxn << 1],son[maxn << 1][maxc];
LL num[maxn << 1];
int size,last;
void Init(){
    size = last = 1;
}
void insert(char c){
    int s = c - ‘a‘;
    int p = last,np = ++size;last = np; num[np] = 1;
    len[np] = len[p] + 1;
    for(;p && !son[p][s]; p = fa[p]) son[p][s] = np;
    if(!p) fa[np] = 1;
    else{
        int q = son[p][s];
        if(len[p] + 1 == len[q]) fa[np] = q;
        else{
            int nq = ++size; len[nq] = len[p] + 1;
            memcpy(son[nq],son[q],sizeof(son[q]));
            fa[nq] = fa[q]; fa[q] = fa[np] = nq;
            for(;son[p][s] == q && p;p = fa[p]) son[p][s] = nq;
        }
    }
}
void insert(char *s){
    Init();
    for(int i = 0; s[i] ; i ++) insert(s[i]);
}
char str[maxn];
struct Edge{
    int to,next;
}edge[maxn << 1];
int head[maxn << 1],tot;
void init(){
    for(int i = 0; i <= size; i ++) head[i] = -1;
    tot = 0;
}
void add(int u,int v){
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void dfs(int t){
    for(int i = head[t]; ~i ; i = edge[i].next){
        int v = edge[i].to;
        dfs(v);
        num[t] += num[v];
    }
}
int main(){
    scanf("%s",str);
    insert(str); init();
    for(int i = 2; i <= size; i ++) add(fa[i],i);
    dfs(1);
    LL ans = 0;
    for(int i = 2; i <= size; i ++) if(num[i] != 1)ans = max(ans,len[i] * num[i]);
    Prl(ans);
    return 0;
}

P1368 工艺

最小表示法,将原串倍增一遍插入,SAM上直接寻找长度为N的字典序最小的路径

因为是倍增了一遍,脑补证明往任意点出发必定能找到长度至少为N的路径,所以甚至不需要dfs,直接跑即可

const int maxn = 6e5 + 10;
int N,M,K;
int len[maxn << 1],fa[maxn << 1];
map<int,int>son[maxn << 1];
int size,last;
void Init(){
    size = last = 1;
}
inline void insert(int c){
    int p = last,np = ++size; last = np;
    len[np] = len[p] + 1;
    for(;p && !son[p].count(c); p = fa[p]) son[p][c] = np;
    if(!p) fa[np] = 1;
    else{
        int q = son[p][c];
        if(len[p] + 1 == len[q]) fa[np] = q;
        else{
            int nq = ++size; len[nq] = len[p] + 1;
            son[nq] = son[q];
            fa[nq] = fa[q]; fa[q] = fa[np] = nq;
            for(;son[p][c] == q && p; p = fa[p]) son[p][c] = nq;
        }
    }
}
int a[maxn];
int main(){
    Sca(N); Init();
    for(int i = 1; i <= N ; i ++) insert(a[i] = read());
    for(int i = 1; i <= N ; i ++) insert(a[i]);
    int t = 1;
    for(int i = 1; i <= N ; i ++){
        printf("%d ",son[t].begin()->first);
        t = son[t].begin()->second;
    }
    return 0;
}

K小子串

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <bitset>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x)
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x)
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
int read(){int x = 0,f = 1;char c = getchar();while (c<‘0‘ || c>‘9‘){if (c == ‘-‘) f = -1;c = getchar();}
while (c >= ‘0‘&&c <= ‘9‘){x = x * 10 + c - ‘0‘;c = getchar();}return x*f;}
const double PI = acos(-1.0);
const double eps = 1e-9;
const int maxn = 5e5 + 10;
const int maxc = 26;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int N,M,K;
int len[maxn << 1],fa[maxn << 1],son[maxn << 1][maxc];
int size,last;
int num[maxn << 1];
void Init(){
    size = last = 1;
}
void insert(char c){
    int s = c - ‘a‘;
    int p = last,np = ++size;last = np;num[np] = 1;
    len[np] = len[p] + 1;
    for(;p && !son[p][s]; p = fa[p]) son[p][s] = np;
    if(!p) fa[np] = 1;
    else{
        int q = son[p][s];
        if(len[p] + 1 == len[q]) fa[np] = q;
        else{
            int nq = ++size; len[nq] = len[p] + 1;
            memcpy(son[nq],son[q],sizeof(son[q]));
            fa[nq] = fa[q]; fa[q] = fa[np] = nq;
            for(;son[p][s] == q && p; p = fa[p]) son[p][s] = nq;
        }
    }
}
void insert(char *s){
    Init();
    for(int i = 0;s[i]; i ++) insert(s[i]);
}
char str[maxn];
int tmp[maxn << 1],A[maxn << 1];
int sum[maxn << 1];
void dfs(int t,int k){
    if(k <= num[t]) return;
    k -= num[t];
    for(int i = 0 ; i < 26; i ++){
        if(!son[t][i]) continue;
        int v = son[t][i];
        if(sum[v] >= k){
            printf("%c",i + ‘a‘);
            dfs(v,k);
            return;
        }
        k -= sum[v];
    }
}
int main(){
    scanf("%s",str);
    insert(str);
    for(int i = 1; i <= size; i ++) tmp[len[i]]++;
    for(int i = 1; i <= size; i ++) tmp[i] += tmp[i - 1];
    for(int i = 1; i <= size; i ++) A[tmp[len[i]]--] = i;
    for(int i = size; i >= 1; i --) num[fa[A[i]]] += num[A[i]];
    int op = read(),k = read();
    for(int i = 1; i <= size; i ++) sum[i] = op?num[i]:num[i] = 1;
    sum[1] = num[1] = 0;
    for(int i = size; i >= 1; i --){
        for(int j = 0 ; j < 26; j ++){
            if(son[A[i]][j]) sum[A[i]] += sum[son[A[i]][j]];
        }
    }
    if(sum[1] < k) puts("-1");
    else dfs(1,k);
    return 0;
}

留坑

原文地址:https://www.cnblogs.com/Hugh-Locke/p/11502775.html

时间: 2024-11-10 18:45:29

后缀自动机(SAM) 合集的相关文章

后缀自动机习题合集

(写的都是初中小朋友czl早就切过的题……) http://www.cnblogs.com/Lyush/p/3281546.html POJ-1509 Glass Beads UVA - 719 Glass Beads 题意:一个字符串可以将第一个字符放到最后一位,然后问不断这样做可以得到的字典序最小的字符串 sam模板题,copy一遍建个sam,然后直接在sam中跑一遍就行了. sam记录了字符串的所有后缀(也随便记录了字串),从root开始到每个接受态节点都是一个后缀(或多个),从root开

浅谈后缀自动机SAM

一下是蒟蒻的个人想法,并不很严谨,仅供参考,如有缺误,敬请提出 参考资料: 陈立杰原版课件 litble 某大神 某大神 其实课件讲得最详实了 有限状态自动机 我们要学后缀自动机,我们先来了解一下自动机到底是什么.[虽说以前也学过AC自动机,只是当一个名字罢了] 有限自动机的功能是识别字符串,作用各不相同 如果自动机A能识别串s,那么A(s) = true 自动机有一个初始状态,从初始状态出发能到达多个状态.到达终止状态表示字符串识别 后缀自动机SAM 我们略去建机原理的分析和建机过程,具体原理

后缀自动机SAM

终于遇到了一道后缀数组不能过 一定要学SAM的题... (看了半个下午+半个上午) 现在总结一下(是给我自己总结..所以只总结了我觉得重要的 .. 看不太懂的话可以To   http://blog.csdn.net/clover_hxy/article/details/53758535  图文并茂 或者 去看更长更详细的陈立杰PPT   http://wenku.baidu.com/link?url=9YEHHchtr0vyGGDZAcsMYPI3l_Q82UNPuS4KqkfrlG_t5NFk

【文文殿下】后缀自动机(SAM)求最长公共子串的方法

首先,在A 串上建立一个SAM,然后用B串在上面跑.具体跑的方法是: 从根节点开始,建立一个指针 p ,指着B串的开头,同步移动指针,沿着SAM的边移动,如果可以移动(即存在边)那么万事皆好,直接len++就好,但是,如果无法继续转移(失配了),那么,我们考虑跳回其父节点,因为其父节点的Right集是当前状态的真超集,那么其父节点状态所代表的字符串的集合中的任意一个字符串,都是当前状态所代表的字符串集合中的正在匹配的字符串(会不会一定是最长串?)的后缀,所以,有一个贪心的思想:父节点状态中的最长

后缀自动机(SAM) :SPOJ LCS - Longest Common Substring

LCS - Longest Common Substring no tags A string is finite sequence of characters over a non-empty finite set Σ. In this problem, Σ is the set of lowercase letters. Substring, also called factor, is a consecutive sequence of characters occurrences at

后缀自动机(SAM)模板

1 struct SAM{ 2 int ch[maxn][26],fa[maxn],len[maxn],cnt,last; 3 void Init() 4 { 5 memset(ch,0,sizeof(ch)); 6 memset(fa,0,sizeof(fa)); 7 last=cnt=1; 8 } 9 void Add(int c) 10 { 11 int p=last,np=last=++cnt; 12 len[np]=len[p]+1; 13 while(!ch[p][c]&&p)

[hdu4436 str2int]后缀自动机SAM(或后缀数组SA)

题意:给n个数字串,求它们的所有不包含前导0的不同子串的值之和 思路:把数字串拼接在一起,构造SAM,然后以每个状态的长度len作为特征值从小到大排序,从前往后处理每个状态,相当于按拓扑序在图上合并计算答案. #include <bits/stdc++.h> using namespace std; #define X first #define Y second #define pb(x) push_back(x) #define mp(x, y) make_pair(x, y) #defi

后缀自动机(SAM):SPOJ Longest Common Substring II

Longest Common Substring II Time Limit: 2000ms Memory Limit: 262144KB A string is finite sequence of characters over a non-empty finite set Σ. In this problem, Σ is the set of lowercase letters. Substring, also called factor, is a consecutive sequenc

HDU 4641 K-string 后缀自动机 并查集

http://acm.hdu.edu.cn/showproblem.php?pid=4641 https://blog.csdn.net/asdfgh0308/article/details/40969047 给一个小写字母字符串,1 a表示在字符串尾部添加一个小写字母a,2 表示当前有多少种子串出现次数大于等于K. 求出现次数桶排序(说是拓扑排序也可以?)就阔以了,种类就是t[i].len-t[t[i].f].len. 在线处理是直接扫描,时间复杂度是O(树高*m). 离线做法是先把所有添加操