Description
Solution
考虑循环同构的性质,每次相当于从最前面删去一个字符,从最后面加上一个字符。在(SAM)里对应着往上跳(fa)或不跳和走对应的字符串转移边。
考虑对于长度为(l)的串,最多删(l)次,最多加(l)次,所以直接在(SAM)上暴力删除添加字符的复杂度就是(O(l))的。
那么先把字符串在后面将自己复制一遍,然后丢到(SAM)上跑,如果没有对应的转移边就不停地跳(fa),直到第一个满足是字符串的循环同构串的节点。那么这时考虑删除最前面的字符,就有两种情况,一种是(len_{fa_{now}} + 1 =)原字符串串长,也就是说,删去最前面的一个字符后,要往上跳(fa);另一种是$len_{fa_{now}} + 1 < (当前匹配长度,这就说明删除一个字符后的)endpos$集合没有改变,直接转移下一个边就行。
然后因为要求本质不同,所以打上时间戳标记,确保每种子串只被算一次贡献即可。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1000000;
int l, fa[N * 2 + 50], son[N * 2 + 50][26], len[N * 2 + 50], vis[N * 2 + 50], siz[N * 2 + 50], num, head[N * 2 + 50], last = 1, cnt = 1;
char st[N * 2 + 50];
struct Node
{
int next, to;
} edge[N * 2 + 50];
void Addedge(int u, int v)
{
edge[++num] = (Node){head[u], v};
head[u] = num;
return;
}
void Insert(int c)
{
int p = last, ne = last = ++cnt;
len[ne] = len[p] + 1; siz[ne] = 1;
while (!son[p][c] && p) son[p][c] = ne, p = fa[p];
if (!p) { fa[ne] = 1; return; }
int q = son[p][c];
if (len[q] == len[p] + 1) { fa[ne] = q; return; }
int sp = ++cnt;
for (int i = 0; i < 26; i++) son[sp][i] = son[q][i];
len[sp] = len[p] + 1;
fa[sp] = fa[q];
fa[q] = fa[ne] = sp;
while (p && son[p][c] == q) son[p][c] = sp, p = fa[p];
return;
}
void Dfs(int x)
{
// cout << x << " len:" << len[x] << " " << endl;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
Dfs(v);
siz[x] += siz[v];
}
// cout << x << " " << siz[x] << endl;
return;
}
int main()
{
scanf("%s", st + 1);
l = strlen(st + 1);
for (int i = 1; i <= l; i++) Insert(st[i] - 'a');
// cout << cnt << endl;
for (int i = 2; i <= cnt; i++) Addedge(fa[i], i);
Dfs(1);
int t;
//for (int i = 1; i <= cnt; i++) printf("%d ", siz[i]); puts("");
scanf("%d", &t);
for (int i = 1; i <= cnt; i++) vis[i] = t;
while (t--)
{
int ans = 0;
scanf("%s", st + 1);
int now = 1, length = 0;
l = strlen(st + 1);
int yl = l;
for (int i = yl + 1; i <= yl * 2 - 1; i++) st[i] = st[i - yl];
l = 2 * l - 1;
for (int i = 1; i <= l; i++)
{
int c = st[i] - 'a';
// cout << st[i] << " " << length << " " << now << endl;
while (!son[now][c] && now) now = fa[now], length = len[now];
if (son[now][c]) length++, now = son[now][c]; else now = 1, length = 0;
if (length == yl)
{
if (vis[now] > t) ans += siz[now], vis[now] = t;
if (len[fa[now]] + 1 == yl) now = fa[now];
length--;
}
// cout << now << endl;
}
printf("%d
", ans);
}
return 0;
}