今天终于把这周的坑填了,同样看了很多博客,这里就不详细总结了,就简单整理一下了。
应用:
1、存在性查询:给定文本T,询问格式如下:给定字符串P,问P是否是T的子串。 直接按着路径走,看是否存在即可
2、不同的子串个数:对于每一个节点即为:len[i] - len[fa[i]] 加和即可
3、不同子串的总长:这里我是通过len[i] 和 fa[] 来求得
4、字典序第k小子串:先保存在每个节点后继的字符串的数目,然后逐步找即可,链接:点击查看
5、最小循环移位:这个还没整理
问题.给定字符串S,找到和它循环同构的字典序最小字符串。
复杂度要求.O(length(S)).
算法.我们将字符串S+S建立后缀自动机。该自动机将包含和S循环同构的所有字符串。
从而,问题就简化成了在自动机中找出字典序最小的,长度为length(S)的路径,这很简单:从初始状态开始,每一步都贪心地走
,经过最小的转移。
6、出现次数查询:按照路径找,num即为次数
7、首次出现位置查询:可以在开一个数组,在插入的时候直接记录即可
8、所有出现位置查询:讲解:https://blog.csdn.net/qq_35649707/article/details/66473069
9、查询不在文本中出现的最短字符串
10、求两个字符串的最长公共子串:for查询串,通过fa回溯查找,链接:点击查看
多个串的:点击查看
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct SAM {
int n; // 字符串长度
int tot; // 节点编号
int len[N << 1]; // 最长子串的长度 (该节点子串数量 = len[x] - len[fa[x]])
int fa[N << 1]; // // 后缀链接 (最短串前部少一个字符所到达的状态)
int cnt[N << 1]; // 被后缀链接的个数
int nex[N << 1][26]; // 下一个状态
int last; // 最后一个节点
ll num[N << 1]; // 该状态子串的数量
ll maxx[N << 1]; // 长度为x的子串出现次数最多的子串的数目
ll sum[N << 1]; // 该节点后面所形成的自字符串的总数
ll ans; // 不同字符串数目
ll sublen; // 不同字符串总长度
int X[N << 1], Y[N << 1]; // Y表示排名为x的节点,X表示该长度前面还有多少个
void init(int x) {
for(int i = 0; i <= x * 2 + 10; i++) {
maxx[i] = 0;
cnt[i] = 0;
num[i] = 0;
X[i] = 0;
for(int j = 0; j <= 25; j++)
nex[i][j] = 0;
}
n = 0;
last = 1;
tot = 1; // 1是起始点 空集
fa[1] = 0;
len[1] = 0;
}
void insert (int c) {
n++;
int x = ++tot; // 新建节点
len[x] = len[last] + 1;
num[x] = 1;
int p;
for(p = last; p && !nex[p][c]; p = fa[p]) nex[p][c] = x; //沿着后缀连接 将所有没有字符c转移的节点直接指向新节点
if(!p) fa[x] = 1, cnt[1]++; // 没有c的转移,直接链接到起点
else {
int q = nex[p][c];
if(len[p] + 1 == len[q]) // p q连续
fa[x] = q, cnt[q]++;
else {
int nq = ++tot; // 不连续 复制一份q的 使得和p连续
len[nq] = len[p] + 1;
fa[nq] = fa[q];
memcpy(nex[nq], nex[q], sizeof(nex[q]));
for(; p && nex[p][c] == q; p = fa[p]) nex[p][c] = nq; //沿着后缀连接 将所有通过c转移为q的改为nq
fa[q] = fa[x] = nq;
cnt[nq] += 2;
}
}
last = x;
}
void getposnum() {
for(int i = 1; i <= tot; i++) X[len[i]]++;
for(int i = 1; i <= tot; i++) X[i] += X[i - 1];
for(int i = 1; i <= tot; i++) Y[X[len[i]]--] = i;
for(int i = tot; i >= 1; i--) num[fa[Y[i]]] += num[Y[i]];
}
void getSUM() {
for(int i = tot;i >= 1; i--) {
sum[Y[i]] = 1;
for(int j = 0 ;j <= 25; j++)
sum[Y[i]] += sum[nex[Y[i]][j]];
}
}
void getsubnum() { // 不同字符串数目
ans = 0;
for(int i = 1; i <= tot; i++)
ans += len[i] - len[fa[i]];
}
void getmaxx() { // 长度为x的子串出现次数最多的子串的数目
// getposnum();
for(int i = 1; i <= tot; i++) maxx[len[i]] = max(maxx[len[i]], num[i]);
for(int i = n - 1; i >= 1; i--) maxx[i] = max(maxx[i], maxx[i + 1]);
}
void getsublen() {
sublen = 0;
for(int i = 1; i <= tot; i++) {
sublen += 1LL * (len[i] + len[fa[i]] + 1) * (len[i] - len[fa[i]]) / 2;
}
// cout << sublen << endl;
}
// 两个串最长公共子串
int compare(char str[]) {
int l = strlen(str);
int res = 0, cnt = 0;
for(int i = 0, p = 1; i < l; i++) {
if(nex[p][str[i] - 'a']) cnt++, p = nex[p][str[i] - 'a'];
else {
while(p && !nex[p][str[i] - 'a']) p = fa[p];
if(!p) cnt = 0, p = 1;
else cnt = len[p] + 1, p = nex[p][str[i] - 'a'];
}
res = max(res, cnt);
}
// printf("%d\n", res);
return ans;
}
/* 多个串
int compare(char str[]) {
int l = strlen(str);
int res = 0, cnt = 0;
for(int i = 0, p = 1; i < l; i++) {
if(nex[p][str[i] - 'a']) cnt++, p = nex[p][str[i] - 'a'];
else {
while(p && !nex[p][str[i] - 'a']) p = fa[p];
if(!p) cnt = 0, p = 1;
else cnt = len[p] + 1, p = nex[p][str[i] - 'a'];
}
dp[p] = max(dp[p], cnt);
}
for(int i = tot; i >= 2; i--)
dp[fa[i]] = max(dp[fa[i]], min(dp[i], len[fa[i]]));
for(int i = 2; i <= tot; i++) {
if(cntans[i] == -1 || dp[i] < cntans[i])
cntans[i] = dp[i];
dp[i] = 0;
}
return res;
}
*/
void get_kth(int k) {
int pos = 1, cnt;
string s = "";
while(k) {
for(int i = 0; i <= 25; i++) {
if(nex[pos][i] && k) {
cnt = nex[pos][i];
if(sum[cnt] < k) k -= sum[cnt];
else {
k--;
pos = cnt;
s += (char)(i + 'a');
break;
}
}
}
}
cout << s << endl;
}
}sam;
char s[N];
int main() {
int T, len;
scanf("%s", s + 1);
len = strlen(s + 1);
sam.init(len);
for(int i = 1; i <= len; i++)
sam.insert(s[i] - 'a');
sam.getposnum();
sam.getSUM();
return 0;
}
推荐博客:
俄译,很详细:https://blog.csdn.net/qq_35649707/article/details/66473069
推荐模板(但我感觉我整理的更好了。。。):https://blog.csdn.net/Anoy_acer/article/details/82932524
学弟鑫爷的各种题型整理:https://blog.csdn.net/zhaoxinxin1234/article/details/97886634#commentBox
有的还没有过题,如有错误,敬请批证。