TJOI2013 单词

题意

给出 \(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;
}
posted @ 2019-10-06 15:06  小蒟蒻hlw  阅读(145)  评论(0)    收藏  举报