给出 \(n\) 个字符串,求出每个字符串在所有字符串中出现的次数。其中 \(n\le200\) , \(\sum len \le 10^6\) 。
考试时第一个想到的是Trie树,但通过样例发现 aaa
中单词 a
也算了3次,不会用Trie树处理......
于是想到KMP,把所有的单词都加到字符串 s[]
中,于是样例中的s数组便是这样子的: aaaaaa
,这样子肯定不行,我们要在每个单词后都加一个 #
,所以s数组就成了这样: a#aa#aaa#
,然后就可以愉快的KMP了。
然而此题正解 AC自动机优化成Trie图,kmp实测 \(90pts\) ,洛谷吸氧 \(100pts\) 。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000003
#define il inline
#define re register
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
int n, len;
int nxt[201][N<<1];
char s[N<<1], ch[201][N];
void pre(int k) {
for (int i = 2, j = 0; ch[k][i]; ++i) {
while (j && ch[k][j+1] != ch[k][i]) j = nxt[k][j];
if (ch[k][j+1] == ch[k][i]) j++;
nxt[k][i] = j;
}
}
int kmp(int k) {
int ans = 0;
for (int i = 1, j = 0; i <= len; ++i) {
while (j && ch[k][j+1] != s[i]) j = nxt[k][j];
if (ch[k][j+1] == s[i]) j++;
if (j == strlen(ch[k] + 1)) ans++, j = nxt[k][j];
}
return ans;
}
int main() {
// File("word");
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%s", ch[i] + 1);
pre(i);
for (int j = 1; ch[i][j]; ++j) s[++len] = ch[i][j];
s[++len] = '#';
}
for (int i = 1; i <= n; ++i) printf("%d\n", kmp(i));
return 0;
}
原文:https://www.cnblogs.com/hlw1/p/11627352.html