E. Three strings 广义后缀自动机
Posted stupid_one
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了E. Three strings 广义后缀自动机相关的知识,希望对你有一定的参考价值。
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("\n"); 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; }
以上是关于E. Three strings 广义后缀自动机的主要内容,如果未能解决你的问题,请参考以下文章
MemSQL Start[c]UP 2.0 - Round 1 E - Three strings 广义后缀自动机