http://codeforces.com/problemset/problem/452/E
多个主串的模型。
建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小。同时也维护一个R[]表示那个串出现过。
所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数。
可以知道sigma dp[i][0...k]是等于 endpos集合的大小。
然后把这个贡献加到min(i)....max(i)中去就可以了
差分一下。
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int MOD = 1e9 + 7; const int maxn = 6e5 + 20, N = 26; struct Node { int mxCnt; //mxCnt表示后缀自动机中当前节点识别子串的最大长度 int miCnt; //miCnt表示后缀自动机中当前节点识别子串的最小长度 int id; //表示它是第几个后缀自动机节点,指向了它,但是不知道是第几个,用id判断 int pos; //pos表示它在原串中的位置。 bool flag; //表示当前节点是否能识别前缀 bool R[3]; // 广义后缀自动机识别此状态是否在第R[i]个主串中出现过 struct Node *pNext[N], *fa; }suffixAutomaton[maxn], *root, *last; //大小需要开2倍,因为有一些虚拟节点 int t; //用到第几个节点 struct Node *create(int mxCnt = -1, struct Node *node = NULL) { //新的节点 if (mxCnt != -1) { suffixAutomaton[t].mxCnt = mxCnt, suffixAutomaton[t].fa = NULL; for (int i = 0; i < N; ++i) suffixAutomaton[t].pNext[i] = NULL; } else { suffixAutomaton[t] = *node; //保留了node节点所有的指向信息。★全部等于node //可能需要注意下pos,在原串中的位置。现在pos等于原来node的pos } suffixAutomaton[t].id = t; //必须要有的,不然id错误 suffixAutomaton[t].flag = false; //默认不是前缀节点 return &suffixAutomaton[t++]; } void addChar(int x, int pos, int id) { //pos表示在原串的位置 struct Node *p = last; if (p->pNext[x] != NULL) { // 有了,就不需要np,广义后缀自动机 struct Node *q = p->pNext[x]; if (p->mxCnt + 1 == q->mxCnt) { last = q; //用来接收后缀字符 q->flag = true; q->R[id] = true; return; } //现在的q没办法成为接受后缀的点 //那么就开一个节点模拟它,所以这个节点是id的前缀节点 struct Node * nq = create(-1, q); for (int i = 0; i < 3; ++i) nq->R[i] = false; nq->mxCnt = p->mxCnt + 1; nq->R[id] = true; nq->flag = true; //这个点是属于id的。是id的前缀节点,因为q不能接受后缀 q->fa = nq; //这里是没有np的 q->miCnt = nq->mxCnt + 1; for (; p && p->pNext[x] == q; p = p->fa) p->pNext[x] = nq; last = nq; //成为接受后缀的节点。 return; } struct Node *np = create(p->mxCnt + 1, NULL); for (int i = 0; i < 3; ++i) np->R[i] = false; //每次都要清空 np->R[id] = true; np->flag = true; //前缀节点 np->pos = pos, last = np; //last是最尾那个可接收后缀字符的点。 for (; p != NULL && p->pNext[x] == NULL; p = p->fa) p->pNext[x] = np; if (p == NULL) { np->fa = root; np->miCnt = 1; // 从根节点引一条边过来 return; } struct Node *q = p->pNext[x]; if (q->mxCnt == p->mxCnt + 1) { //中间没有任何字符,可以用来代替接受后缀、 np->fa = q; np->miCnt = q->mxCnt + 1; // q是状态8的"ab",np是状态7的"bab"长度是2+1 return; } struct Node *nq = create(-1, q); // 新的q节点,用来代替q,帮助np接收后缀字符 for (int i = 0; i < 3; ++i) nq->R[i] = false; nq->mxCnt = p->mxCnt + 1; //就是需要这样,这样中间不包含任何字符 q->miCnt = nq->mxCnt + 1, np->miCnt = nq->mxCnt + 1; q->fa = nq, np->fa = nq; //现在nq是包含了本来q的所有指向信息 for (; p && p->pNext[x] == q; p = p->fa) { p->pNext[x] = nq; } } void init() { t = 0; root = last = create(0, NULL); } char str[maxn]; LL dp[maxn][3]; queue<int> que; int in[maxn]; LL ans[maxn]; void work() { init(); int len = inf; for (int i = 0; i < 3; ++i) { last = root; scanf("%s", str + 1); int t = 0; for (int j = 1; str[j]; ++j) { t++; addChar(str[j] - 'a', j, i); } len = min(len, t); } for (int i = 1; i < t; ++i) { in[suffixAutomaton[i].fa->id]++; // if (suffixAutomaton[i].flag) { for (int j = 0; j < 3; ++j) { dp[i][j] = suffixAutomaton[i].R[j]; } // } } for (int i = 1; i < t; ++i) { if (in[i] == 0) { que.push(i); } } while (!que.empty()) { int cur = que.front(); que.pop(); if (!cur) break; for (int i = 0; i < 3; ++i) { dp[suffixAutomaton[cur].fa->id][i] += dp[cur][i]; } in[suffixAutomaton[cur].fa->id]--; if (in[suffixAutomaton[cur].fa->id] == 0) que.push(suffixAutomaton[cur].fa->id); } for (int i = 1; i < t; ++i) { LL res = 1; for (int j = 0; j < 3; ++j) { res = res * dp[i][j] % MOD; } // printf("%lld ", res); int en = suffixAutomaton[i].mxCnt; int be = suffixAutomaton[i].miCnt; ans[en + 1] = (ans[en + 1] - res + MOD) % MOD; ans[be] = (ans[be] + res) % MOD; } // printf(" "); for (int i = 1; i <= len; ++i) { ans[i] = (ans[i] + ans[i - 1] + MOD) % MOD; } for (int i = 1; i <= len; ++i) { printf("%I64d ", ans[i]); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 3e5 + 22, N = 27; const int MOD = 1e9 + 7; struct SAM { int mxCnt[maxn << 1], son[maxn << 1][N], fa[maxn << 1], pos[maxn << 1]; int flag[maxn << 1][3]; //是否前缀节点 int root, last, DFN, t; int create() { ++t; mxCnt[t] = pos[t] = fa[t] = NULL; for (int i = 0; i < 3; ++i) flag[t][i] = NULL; for (int i = 0; i < N; ++i) son[t][i] = NULL; return t; } void init() { DFN = 1; ++DFN; t = 0, root = 1; last = create(); } void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置 int p = last; if (son[p][x]) { //已经存在,广义后缀自动机 int q = son[p][x]; if (mxCnt[p] + 1 == mxCnt[q]) { last = q; flag[q][id] = DFN; return; } int nq = create(); for (int i = 0; i < N; ++i) son[nq][i] = son[q][i]; fa[nq] = fa[q]; pos[nq] = pos[q]; mxCnt[nq] = mxCnt[p] + 1; flag[nq][id] = DFN; fa[q] = nq; for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq; last = nq; return; } int np = create(); last = np; mxCnt[np] = mxCnt[p] + 1, pos[np] = _pos, flag[np][id] = DFN; //前缀节点 for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np; if (p == NULL) { fa[np] = root; return; } int q = son[p][x]; if (mxCnt[q] == mxCnt[p] + 1) { fa[np] = q; return; } int nq = create(); //用来代替q的,默认不是前缀节点 flag[nq][id] = DFN - 1; //默认不是前缀节点 pos[nq] = pos[q]; //pos要和q相同 for (int i = 0; i < N; ++i) son[nq][i] = son[q][i]; fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + 1; fa[q] = nq, fa[np] = nq; for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq; } int dp[maxn << 1][3], in[maxn << 1], que[maxn << 1]; void topo() { //多次使用不用清空 int head = 0, tail = 0; for (int i = 2; i <= t; ++i) { for (int j = 0; j < 3; ++j) { dp[i][j] = flag[i][j] == DFN; } in[fa[i]]++; } for (int i = 2; i <= t; ++i) { if (in[i] == 0) que[tail++] = i; } while (head < tail) { int cur = que[head++]; if (cur == root) break; for (int i = 0; i < 3; ++i) dp[fa[cur]][i] += dp[cur][i]; in[fa[cur]]--; if (in[fa[cur]] == 0) que[tail++] = fa[cur]; } } } sam; char str[maxn], sub[maxn]; LL ans[maxn << 1]; void work() { int len = inf; sam.init(); for (int i = 0; i < 3; ++i) { sam.last = sam.root; scanf("%s", str + 1); int t = strlen(str + 1); len = min(len, t); for (int j = 1; str[j]; ++j) { sam.addChar(str[j] - 'a', j, i); } } sam.topo(); for (int i = 2; i <= sam.t; ++i) { LL res = 1; for (int j = 0; j < 3; ++j) { res = res * sam.dp[i][j] % MOD; // printf("%d ", sam.dp[i][j]); } // printf(" "); ans[sam.mxCnt[sam.fa[i]] + 1] = (ans[sam.mxCnt[sam.fa[i]] + 1] + res) % MOD; ans[sam.mxCnt[i] + 1] = (ans[sam.mxCnt[i] + 1] - res + MOD) % MOD; } for (int i = 1; i <= len; ++i) ans[i] = (ans[i] + ans[i - 1] + MOD) % MOD; for (int i = 1; i <= len; ++i) { printf("%I64d ", ans[i]); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }
也可以直接sam,把三个串加入到sam中,中间用特殊符号分割
然后dp[i][j]表示到达第i个状态,能得到的j号串的个数是多少即可
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; const int maxn = 3e5 + 20, N = 27; struct SAM { int mxCnt[maxn << 1], son[maxn << 1][N], fa[maxn << 1], pos[maxn << 1]; // int flag[maxn << 1][3]; //是否前缀节点 int flag[maxn << 1][3]; int root, last, DFN, t; int create() { ++t; mxCnt[t] = pos[t] = fa[t] = NULL; for (int i = 0; i < N; ++i) son[t][i] = NULL; return t; } void init() { ++DFN; t = 0, root = 1; last = create(); } void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置 int p = last; int np = create(); last = np; mxCnt[np] = mxCnt[p] + 1, pos[np] = 1 << _pos; //前缀节点 flag[np][id] = DFN; for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np; if (p == NULL) { fa[np] = root; return; } int q = son[p][x]; if (mxCnt[q] == mxCnt[p] + 1) { fa[np] = q; return; } int nq = create(); //用来代替q的,默认不是前缀节点 pos[nq] = pos[q]; //pos要和q相同 for (int i = 0; i < N; ++i) son[nq][i] = son[q][i]; fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + 1; fa[q] = nq, fa[np] = nq; for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq; } int dp[maxn << 1][3], in[maxn << 1], que[maxn << 1]; void topo() { for (int i = 2; i <= t; ++i) { in[fa[i]]++; for (int j = 0; j < 3; ++j) { dp[i][j] = flag[i][j] == DFN; } } int head = 0, tail = 0; for (int i = 2; i <= t; ++i) { if (in[i] == 0) que[tail++] = i; } while (head < tail) { int cur = que[head++]; if (cur == root) break; pos[fa[cur]] |= pos[cur]; for (int j = 0; j < 3; ++j) { dp[fa[cur]][j] += dp[cur][j]; } in[fa[cur]]--; if (in[fa[cur]] == 0) que[tail++] = fa[cur]; } } } sam; char str[maxn]; LL ans[maxn]; const int MOD = 1e9 + 7; void work() { sam.init(); int len = inf; for (int i = 0; i < 3; ++i) { scanf("%s", str + 1); int t = 0; for (int j = 1; str[j]; ++j) { t++; sam.addChar(str[j] - 'a', i, i); } len = min(len, t); sam.addChar(26, 0, 0); } sam.topo(); for (int i = 2; i <= sam.t; ++i) { if (sam.pos[i] != 7) continue; LL res = 1; for (int j = 0; j < 3; ++j) { res = res * sam.dp[i][j] % MOD; } int be = sam.mxCnt[sam.fa[i]] + 1; int en = sam.mxCnt[i]; ans[be] = (ans[be] + res) % MOD; ans[en + 1] = (ans[en + 1] - res + MOD) % MOD; } for (int i = 1; i <= len; ++i) ans[i] = (ans[i] + ans[i - 1] + MOD) % MOD; for (int i = 1; i <= len; ++i) { printf("%lld ", ans[i]); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }