据说是 SAM 的板子。。
题意是求循环同构,并且去重。
循环同构的定义是,aa的循环同构是aa,ab的循环同构是ab,ba
我们很显然,可以往最后面加上第一个字符,做 (length) 次,就好了。
考虑怎么删除,由于 SAM 的不同子串个数是 (sum len_i - len_{fa_i})
其实实际上是 (i) 这个节点包含了 (len_{fa_i}+1) ~ (len_i) 的所有节点,且后缀相同,并且 (fa_i) 对应的字符串是 (i) 的子串,我们删掉这个字符,相当于长度减一。
它是把单链缩成一个点,实际上我们可以抽象的考虑,当他的长度等于 (len_{fa_i}) 时,我们就可以直接跳到 (fa_i) 这个位置,显然,后缀相同,不证明了。
当长度等于 (length) 的时候才计算贡献。
#include <bits/stdc++.h>
using namespace std;
int read() {
int x = 0;
char c = getchar();
while (c < 48) c = getchar();
while (c > 47) x = x * 10 + (c - 48), c = getchar();
return x;
}
const int maxn = 2e6 + 62;
char s[maxn];
struct SAM {
int ch[maxn][26], len[maxn], fa[maxn], sz[maxn];
int las, cnt;
SAM() { las = cnt = 1; }
void ins(int c) {
int p = las, np = las = ++cnt;
sz[np] = 1, len[np] = len[p] + 1;
for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if (!p) {
fa[np] = 1;
} else {
int q = ch[p][c];
if (len[q] == len[p] + 1) {
fa[np] = q;
} else {
int nq = ++cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[nq] = fa[q], fa[q] = fa[np] = nq, len[nq] = len[p] + 1;
for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
}
vector<int> g[maxn];
void dfs(int u) {
for (int v : g[u]) dfs(v), sz[u] += sz[v];
}
void insert(int len) {
for (int i = 1; i <= len; i++) ins(s[i] - 'a');
for (int i = 2; i <= cnt; i++) g[fa[i]].push_back(i);
dfs(1);
}
int vis[maxn];
void solve(int Len , int qwq) {
int p = 1, l = 0, ans = 0;
for (int i = 1; i <= Len; i++) {
int c = s[i] - 'a';
while (p && !ch[p][c]) l = len[p = fa[p]];
if (ch[p][c]) {
++l, p = ch[p][c];
}
}
if (l == Len && vis[p] < qwq) {
vis[p] = qwq, ans += sz[p];
}
for (int i = 1; i <= Len; i++) {
int c = s[i] - 'a';
while (p && !ch[p][c]) l = len[p = fa[p]];
if (ch[p][c]) {
++l, p = ch[p][c];
}
if (l > Len && --l == len[fa[p]]) p = fa[p];
if (l == Len && vis[p] < qwq) {
vis[p] = qwq, ans += sz[p];
}
}
printf("%d
", ans);
}
} sam;
signed main() {
scanf("%s", s + 1);
sam.insert(strlen(s + 1));
int _ = read(), qwq = 0;
while (_--) scanf("%s", s + 1), sam.solve(strlen(s + 1), ++ qwq);
return 0;
}