luogu P3966 [TJOI2013]单词

P3966 [TJOI2013]单词

题目大意

给出若干个字符串,求出每个字符串在所有字符串中出现的个数

题解

直接建出AC自动机,然后把fail(next)树抽离出来把边反向,然后一个字符串出现的个数就是它对应fail树上的节点的子树大小(这个画一下图还是很容易理解的吧
因为AC自动机上失配的点会指向当前点,就说明当前点对应的字符串在那个失配的点对应的字符串上出现了一次,那把fail抽出来,然后反向建边就会变成一棵树
然后出现的次数就是子树大小(size和)
看代码好理解一些
code:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define N 1000005
#define C 26
using namespace std;
int py[N], size[N], ch[N][C], vis[N], nxt[N], ha[N], n, tot;
string st;
void insert(int id){
	int p = 0, len = st.length();
	for(int i = 0; i < len; i ++){
		if(!ch[p][st[i] - 'a']) ch[p][st[i] - 'a'] = ++ tot;
		p = ch[p][st[i] - 'a'];
		size[p] ++;
	}
	py[id] = p;//映射到树上的节点
}
queue<int> q;
void build(){
	for(int i = 0; i < C; i ++) if(ch[0][i]) q.push(ch[0][i]);
	while(q.size()){
		int u = q.front(); q.pop();
		ha[++ ha[0]] = u;//记下按照宽度优先搜索遍历树的节点访问顺序
		for(int i = 0; i < C; i ++){
			if(ch[u][i]){
				nxt[ch[u][i]] = ch[nxt[u]][i];
				q.push(ch[u][i]);
			} else ch[u][i] = ch[nxt[u]][i];
		}
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1; i <= n; i ++){
		cin >> st;
		insert(i);
	}
	build();
	for(int i = ha[0]; i >= 1; i --) size[nxt[ha[i]]] += size[ha[i]];//这里就是统计子树大小,因为build的时候就是按照树的宽度遍历顺序,所以直接从后往前推就好了(从叶子节点往上推,相当于按top序)
	for(int i = 1; i <= n; i ++) cout << size[py[i]] << endl;//输出即可
	return 0;
}
posted @ 2019-08-09 16:40  lahlah  阅读(21)  评论(0编辑  收藏  举报