Codeforces 452E Three Strings(后缀自动机)
上学期很认真地学了一些字符串的常用工具,各种 suffix structre,但是其实对后缀自动机这个部分是理解地不太透彻的,以致于看了师兄A这题的代码后,我完全看不懂,于是乎重新看回一些学习后缀自动机的博客和代码,重新理解了一些概念,感觉自己对于后缀自动机的理解深了一些。
先说说题意,给了三个串,问对于长度为x的串,满足(i1,i2,i3)(s1[i1..i1+k-1]==s2[i2...i2+k-1]==s3[i3+i3+k-1])有多少组,要输出全部的组数。
师兄的代码的做法是这样的,插入三个串到后缀自动机上,中间用不同的分隔符隔开。然后对于每个状态求它的right集合的大小,求的是该字符串在三个串里的right集合的大小。具体的处理方法好像是这样的,因为有了分隔符,所以如果存在到达分隔符的后继,说明该状态是当前字符串的1个后缀,初始化为1,然后就dfs一遍,将right集合加回去。 此时对于每个状态right[0]*right[1]*right[2]即是该集合表示的字符串的三元对的大小,那么这样的三元对可以更新多少呢? 由后缀自动机的知识可以知道,对于状态s,它可以更新长度为min(s),max(s)的大小,max(s)就是下面的val,而后缀自动机的性质是min(s)=max(pre)-1。所以对于每个三元对段更,由于是先更新再询问,所以可以先打上标记,然后再回加,这样就可以求出正确的答案了。下面贴一记从师兄那里学习到的代码
#pragma warning(disable:4996) #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <vector> #include <string> using namespace std; #define mod 1000000007 #define ll long long #define maxn 610000 struct State{ State *suf, *go[30]; int val;;ll right[3]; State() :suf(0), val(0){ memset(go, 0, sizeof(go)); } }*root,*last; State statePool[maxn], *cur; void init(){ cur = statePool; root = last = cur++; } void extend(int w){ State *p = last, *np = cur++; np->val = p->val + 1; while (p&&!p->go[w]) p->go[w] = np, p = p->suf; if (!p) np->suf = root; else{ State *q = p->go[w]; if (p->val + 1 == q->val) np->suf = q; else{ State *nq = cur++; memcpy(nq->go, q->go, sizeof q->go); nq->val = p->val + 1; nq->suf = q->suf; q->suf = nq; np->suf = nq; while (p&&p->go[w] == q){ p->go[w] = nq, p = p->suf; } } } last = np; } char str[maxn]; int n; bool vis[maxn]; void dfs(State *u){ if (vis[u - statePool]) return; if (!vis[u - statePool]){ vis[u - statePool] = true; for (int i = 0; i < 3; i++){ if (u->go[26 + i]) u->right[i] = 1; } for (int i = 0; i < 26; i++){ if (u->go[i]) { dfs(u->go[i]); for (int k = 0; k < 3; k++){ u->right[k] = (u->right[k] + u->go[i]->right[k]) % mod; } } } } } ll num[maxn]; int main() { init(); int n = 1e8; scanf("%s", str); n = min(n, int(strlen(str))); for (int i = 0; str[i]; i++) extend(str[i] - 'a'); extend(26); scanf("%s", str); n = min(n, int(strlen(str))); for (int i = 0; str[i]; i++) extend(str[i] - 'a'); extend(27); scanf("%s", str); n = min(n, int(strlen(str))); for (int i = 0; str[i]; i++) extend(str[i] - 'a'); extend(28); dfs(root); int tot = cur - statePool; for (int i = 1; i < tot; i++){ State *u = &statePool[i]; ll tmp = u->right[0] % mod * u->right[1] % mod*u->right[2] % mod; num[u->val] = (num[u->val] + tmp) % mod; num[u->suf->val] = ((num[u->suf->val] - tmp) % mod + mod) % mod; } for (int i = tot-2; i >= 1; i--){ num[i] = (num[i] + num[i + 1]) % mod; } for (int i = 1; i <= n; i++){ if (i>1) printf(" "); printf("%I64d", num[i]); } puts(""); }