【CodeChef-LYRC】Music & Lyrics【AC自动机】

【题目链接】

给出W个模板串和N个匹配串,问每个模板串在所有匹配串中一共出现了多少次。


先把所有模板串放进AC自动机,然后用每个匹配串在AC自动机上跑,走过的节点权值++。最后在fail树上做个前缀和,查询每个模板串的结尾节点的权值就好了。


一开始求前缀和写了深搜,但是看了策爷代码,发现可以利用求fail时的广搜队列来跑前缀和,orz%%%。


模板串和匹配串长度不一样,数组开小了WA了一发。

/* Pigonometry */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 505, maxnode = 2500005, maxq = maxnode;

int n, val[maxn], fail[maxnode], son[maxnode][63], sum[maxnode], acmcnt, q[maxq], h, t;
char str[50005];

inline int get(char ch) {
	if(ch >= 'a' && ch <= 'z') return ch - 'a';
	if(ch >= 'A' && ch <= 'Z') return ch - 'A' + 26;
	if(ch >= '0' && ch <= '9') return ch - '0' + 52;
	if(ch == '-') return 62;
}

inline int insert() {
	int now = 0, len = strlen(str);
	for(int i = 0; i < len; i++) {
		int &pos = son[now][get(str[i])];
		if(!pos) pos = ++acmcnt;
		now = pos;
	}
	return now;
}

inline void getfail() {
	h = 0, t = 0;
	for(int i = 0; i < 63; i++) if(son[0][i]) q[t++] = son[0][i];
	while(h != t) {
		int u = q[h++];
		for(int i = 0; i < 63; i++)
			if(!son[u][i]) son[u][i] = son[fail[u]][i];
			else fail[q[t++] = son[u][i]] = son[fail[u]][i];
	}
}

inline void work() {
	int now = 0, len = strlen(str);
	for(int i = 0; i < len; i++) {
		now = son[now][get(str[i])];
		sum[now]++;
	}
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%s", str);
		val[i] = insert();
	}
	getfail();
	int T; scanf("%d", &T);
	while(T--) {
		scanf("%s", str);
		work();
	}
	for(int i = t - 1; i >= 0; i--) sum[fail[q[i]]] += sum[q[i]];
	for(int i = 1; i <= n; i++) printf("%d\n", sum[val[i]]);
	return 0;
}


你可能感兴趣的:(【CodeChef-LYRC】Music & Lyrics【AC自动机】)