题目大意:给你三个字符串$A,B,C$,令$L=min(|A|,|B|,|C|)$,对每个$iin[1,L]$,求出符合$A_{[a,a+i)}=B_{[b,b+i)}=C_{[c,c+i)}$的三元组$(a,b,c)$的个数
题解:先建一棵广义$SAM$,求出每个点可以到达的$A,B,C$的字串的个数,然后这个点贡献的值就是三个串分别的个数乘起来,发现一个点会对$[R_{fail_p+1},R_{p}]$的区间产生贡献,可以差分一下维护。
卡点:无
C++ Code:
#include <algorithm> #include <cstdio> #include <cstring> #define maxn 300010 const int mod = 1e9 + 7; inline void reduce(int &x) {x += x >> 31 & mod;} int n = 0x3f3f3f3f; int ans[maxn]; namespace SAM { #define N (maxn * 3 << 1) int R[N], fail[N], nxt[N][26]; int lst = 1, idx = 1, sz[N][3]; void append(int ch, int tg) { int p = lst, np = lst = ++idx; R[np] = R[p] + 1, sz[np][tg] = 1; for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np; if (!p) fail[np] = 1; else { int q = nxt[p][ch]; if (R[p] + 1 == R[q]) fail[np] = q; else { int nq = ++idx; fail[nq] = fail[q], R[nq] = R[p] + 1, fail[q] = fail[np] = nq; std::copy(nxt[q], nxt[q] + 26, nxt[nq]); for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq; } } } int head[N], cnt; struct Edge { int to, nxt; } e[N]; inline void addedge(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } void dfs(int u) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; dfs(v); sz[u][0] += sz[v][0], sz[u][1] += sz[v][1], sz[u][2] += sz[v][2]; } } void work() { for (int i = 2; i <= idx; i++) addedge(fail[i], i); dfs(1); for (int i = 2; i <= idx; i++) { int tmp = static_cast<long long> (sz[i][0]) * sz[i][1] % mod * sz[i][2] % mod; reduce(ans[R[fail[i]] + 1] += tmp - mod), reduce(ans[R[i] + 1] -= tmp); } for (int i = 2; i <= n; i++) reduce(ans[i] += ans[i - 1] - mod); } #undef N } char s[maxn]; int main() { for (int i = 0; i < 3; i++) { scanf("%s", s); SAM::lst = 1; n = std::min(n, static_cast<int> (strlen(s))); for (char *ch = s; *ch; ++ch) SAM::append(*ch - 'a', i); } SAM::work(); for (int i = 1; i <= n; i++) { printf("%d", ans[i]); putchar(i == n ? ' ' : ' '); } return 0; }