题意:给一个主串,再给出多个模式串,分别求主串中有多少个连续子串,与模式串循环同构。
题解:后缀自动机
因为要求循环同构,所以将模式串复制放到后面。(要么加终止符,要么传入长度)
先对主串建sam,然后跑拓扑,自底向上更新
c
n
t
cnt
cnt(
t
o
p
s
a
m
[
]
topsam[]
topsam[]),然后求
L
C
S
LCS
LCS。
如果长度大于等于
l
l
l,跳
f
a
fa
fa指针到长度为
l
l
l的节点 while (p->fa && p->fa->len >= l) p = p->fa;
。那么此时状态所表示的子串个数
c
n
t
cnt
cnt即为所求,因为状态可能重复,用
v
i
s
vis
vis标记一下有没有被访问过即可 if (p->vis != id) p->vis = id, ans += p->cnt;
。
记得初始化
n
o
d
e
node
node中新增变量。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int CHAR = 26;
const int MAXN = 1000010;
struct SAM_Node {
SAM_Node* fa, * next[CHAR];
int len;
int id, pos;
int vis, cnt;
SAM_Node() {}
SAM_Node(int _len) {
fa = 0;
len = _len;
memset(next, 0, sizeof(next));
}
};
SAM_Node SAM_node[MAXN * 2], * SAM_root, * SAM_last, * topsam[MAXN * 2];
int SAM_size, topcnt[MAXN * 2];
SAM_Node* newSAM_Node(int len) {
SAM_node[SAM_size] = SAM_Node(len);
SAM_node[SAM_size].id = SAM_size;
SAM_node[SAM_size].cnt = SAM_node[SAM_size].vis = 0;
return &SAM_node[SAM_size++];
}
SAM_Node* newSAM_Node(SAM_Node* p) {
SAM_node[SAM_size] = *p;
SAM_node[SAM_size].id = SAM_size;
SAM_node[SAM_size].cnt = SAM_node[SAM_size].vis = 0;
return &SAM_node[SAM_size++];
}
void SAM_init() {
SAM_size = 0;
SAM_root = SAM_last = newSAM_Node(0);
SAM_node[0].pos = 0;
}
void SAM_add(int x, int len) { //len从1开始
SAM_Node* p = SAM_last, * np = newSAM_Node(p->len + 1);
np->pos = len;
SAM_last = np;
for (; p && !p->next[x]; p = p->fa)
p->next[x] = np;
if (!p) {
np->fa = SAM_root;
return;
}
SAM_Node* q = p->next[x];
if (q->len == p->len + 1) {
np->fa = q;
return;
}
SAM_Node* nq = newSAM_Node(q);
nq->len = p->len + 1;
q->fa = np->fa = nq;
for (; p && p->next[x] == q; p = p->fa) p->next[x] = nq;
}
void SAM_build(char* s) {
SAM_init();
int len = strlen(s);
for (int i = 0; i < len; i++) SAM_add(s[i] - 'a', i + 1);
SAM_Node* p = SAM_root;
for (int i = 0; i < len; i++) {
p = p->next[s[i] - 'a'];
p->cnt = 1;
}
}
void topo() {
memset(topcnt, 0, sizeof(topcnt));
for (int i = 0; i <= SAM_size; i++) topcnt[SAM_node[i].len]++;
for (int i = 1; i <= SAM_size; i++) topcnt[i] += topcnt[i - 1];
for (int i = 0; i <= SAM_size; i++) topsam[--topcnt[SAM_node[i].len]] = &SAM_node[i];
}
void LCS(char* s, int id, int l) {
int ans = 0, len = 0;
SAM_Node* p = SAM_root;
//cout << strlen(s) << endl;
for (int i = 0; i < (l << 1) - 1; i++) {
int idx = s[i] - 'a';
if (p->next[idx]) p = p->next[idx], len++;
else {
while (p && !p->next[idx]) p = p->fa;
if (!p) p = SAM_root, len = 0;
else len = p->len + 1, p = p->next[idx];
}
//ans = max(ans, len);
if (len >= l) {
while (p->fa && p->fa->len >= l) p = p->fa;
if (p->vis != id) p->vis = id, ans += p->cnt;
}
}
printf("%d\n", ans);
}
char s[MAXN], t[MAXN];
int q;
int main() {
scanf("%s%d", s, &q);
SAM_build(s);
topo();
for (int i = SAM_size; i >= 2; i--) topsam[i]->fa->cnt += topsam[i]->cnt;
while (q--) {
scanf("%s", t);
int len = strlen(t);
for (int i = len; i < (len << 1) - 1; i++) t[i] = t[i - len];
LCS(t, q + 1, len);
}
return 0;
}