[Ybt Advanced 2-5-2] [luogu P3966] Word frequency / word

Word frequency / word

Topic link: ybt high-efficiency advanced 2-5-2 / luogu P3966

General idea

There are n words, and then ask you how many times each word appears in these words.

Ideas

When we see matching words, and then multiple matches, we will think of AC automata.

But AC automata is how many times multiple words appear in a word, you can't enumerate the past directly.

That's actually pretty good. When you insert it, no matter how many of the previous ones are inserted, add the number of occurrences to it.
In this way, every time you run to this point while failing, it means that the distance from this position to the previous one must be a matching string.

Then we can get the answer by using the reverse topological order to find the prefix sum on the fail edge tree for insurance.

You only need to record the position of the end of the word when inserting the word.

Code

#include<queue>
#include<cstdio>
#include<cstring>

using namespace std;

struct Trie {
    
    
	int son[31], num, fail;
}tree[1000301];
int n, tot, dy[201];
int tp[1000301], tmp;
char c[1000001];
queue <int> q;

void insert(int op) {
    
    //建 Trie 树
	int size = strlen(c);
	int now = 0;
	
	for (int i = 0; i < size; i++) {
    
    
		int nxt = c[i] - 'a';
		if (!tree[now].son[nxt]) tree[now].son[nxt] = ++tot;
		tree[now].num++;
		now = tree[now].son[nxt];
	}
	
	tree[now].num++;
	dy[op] = now;//记录答案的位置
}

void make_fail() {
    
    //建 AC 自动机的 fail 边
	for (int i = 0; i < 26; i++)
		if (tree[0].son[i]) {
    
    
			q.push(tree[0].son[i]);
			tp[++tmp] = tree[0].son[i];
			tree[tree[0].son[i]].fail = 0;
		}
	
	while (!q.empty()) {
    
    
		int now = q.front();
		q.pop();
		
		for (int i = 0; i < 26; i++)
			if (tree[now].son[i]) {
    
    
				q.push(tree[now].son[i]);
				tp[++tmp] = tree[now].son[i];
				tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
			}
			else tree[now].son[i] = tree[tree[now].fail].son[i];
	}
}

void work_tp() {
    
    //直接跑逆序拓扑
	for (int i = tmp; i >= 1; i--) {
    
    
		tree[tree[tp[i]].fail].num += tree[tp[i]].num;
	}
}

int main() {
    
    
	scanf("%d", &n);
	
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%s", &c);
		insert(i);
	}
	
	make_fail();
	
	work_tp();
	
	for (int i = 1; i <= n; i++)
		printf("%d\n", tree[dy[i]].num);
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/114378889