Description:
小张最近在忙毕业设计,所以一直在读论文。一篇论文是由许多单词组成的。
但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。
单词数不超过200
单词总长度不超过1000000
题解:
这题我能想到的做法有三个。
第一个是最朴素的AC自动机。
我们需要清楚知道AC自动机的fail链的定义:走到x所代表的字符串有出现的最长的一个后缀。
在原trie上往子树走可以看作往后面加字符,在fail树上,往子树中走相当于往前加字符。
对每个点求出其是多少个单词的前缀,那么单词所对应点的fail子树的这个和就是答案。
第二个是很容易想的后缀自动机。
这个广义后缀自动机需要宽搜建,不然空点的问题就需要重新跑一遍了。
第三个是把全部的单词拼在一起,用特殊符号间隔,前后缀数组,求height,再用线段树二分的性质去搞。
因为单词数很小,所以这个方法也是可以的。
Code(AC自动机):
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int N = 1e6 + 5;
int n, m, ed[N]; char str[N];
int son[N][26], fail[N], d[N], s[N], tot = 1;
int main() {
scanf("%d", &n);
fo(i, 1, n) {
scanf("%s", str + 1); m = strlen(str + 1);
int x = 1;
fo(j, 1, m) {
int c = str[j] - 'a';
if(!son[x][c]) son[x][c] = ++ tot;
x = son[x][c]; s[x] ++;
}
ed[i] = x;
}
d[0] = d[1] = 1;
fo(i, 0, 25) son[0][i] = 1;
fo(i, 1, d[0]) {
int x = d[i];
fo(j, 0, 25) if(son[x][j]) {
int y = son[x][j], z = fail[x];
while(!son[z][j]) z = fail[z];
fail[y] = son[z][j]; d[++ d[0]] = y;
}
}
fd(i, d[0], 1) s[fail[d[i]]] += s[d[i]];
fo(i, 1, n) printf("%d\n", s[ed[i]]);
}
Code(后缀自动机):
#include<cstdio>
#include<cstring>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 2e6 + 5;
int last[N], ed[N], s[N];
struct Trie {
int tot, son[N][26];
} t;
struct suffix_automation {
int la, tot, son[N][26], fa[N], dep[N];
#define push(x) dep[++ tot] = x;
void add(int c) {
push(dep[la] + 1);
int p = la, np = tot;
for(; p && !son[p][c]; p = fa[p]) son[p][c] = np;
if(!p) fa[np] = 1; else {
int q = son[p][c];
if(dep[p] + 1 < dep[q]) {
push(dep[p] + 1); int nq = tot;
memcpy(son[nq], son[q], sizeof son[q]);
fa[nq] = fa[q]; fa[q] = fa[np] = nq;
for(; son[p][c] == q; p = fa[p]) son[p][c] = nq;
} else fa[np] = q;
}
la = np;
}
int d[N], r[N];
void Turpo() {
fo(i, 1, tot) r[fa[i]] ++;
fo(i, 1, tot) if(!r[i]) d[++ d[0]] = i;
fo(i, 1, d[0]) {
int x = d[i];
if(!(-- r[fa[x]])) d[++ d[0]] = fa[x];
s[fa[x]] += s[x];
}
}
} suf;
int n, m, d[N], ss[N]; char str[N];
int main() {
suf.tot = t.tot = last[1] = 1;
scanf("%d", &n);
fo(i, 1, n) {
scanf("%s", str + 1); m = strlen(str + 1);
int x = 1;
fo(j, 1, m) {
int c = str[j] - 'a';
if(!t.son[x][c]) t.son[x][c] = ++ t.tot;
x = t.son[x][c];
ss[x] ++;
}
ed[i] = x;
}
d[0] = d[1] = 1;
fo(i, 1, d[0]) {
int x = d[i];
fo(j, 0, 25) if(t.son[x][j]) {
d[++ d[0]] = t.son[x][j];
suf.la = last[x];
suf.add(j);
last[t.son[x][j]] = suf.la;
s[last[t.son[x][j]]] += ss[t.son[x][j]];
}
}
suf.Turpo();
fo(i, 1, n) printf("%d\n", s[last[ed[i]]]);
}