题目
给定个单词,这个单词形成一篇文章(单词间隔断).统计每个单词出现次数.
数据范围:
题解
可以看出每个单词的贡献是独立的,也是对整体有影响的,便想到用树来统计贡献.
如果插入一个单词(如),则每层计数的都.这样就统计了所有前缀的贡献.如没有计算.
考虑自动机的指针的转移:(即是的后缀)
如果在插入所有单词后建立指针,那么可以知道一个单词如的所有后缀(如果后缀有贡献)会通过指针链接起来
形成了一棵树,进行树形即可求出所有的贡献
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
struct Trie {
int next[26];
int fail, cnt, sum;
} trie[500005];
int cnt_trie;
void insert(string );
void getfail();
struct edge {
int next, to;
} e[500005];
int head[500005], cnt_adj;
void add_edge(int, int);
bool vis[500005];
void getsum(int);
int find(string);
int N;
string s[205];
int main(){
cin >> N;
int i;
for(i = 1; i <= N; i++){
cin >> s[i];
insert(s[i]);
}
getfail();
getsum(0);
for(i = 1; i <= N; i++)
cout << find(s[i]) << endl;
return 0;
}
void insert(string s){
int i, len = s.size();
int id, u = 0;
for(i = 0; i < len; i++){
id = s[i] - 'a';
if(!trie[u].next[id])
trie[u].next[id] = ++cnt_trie;
u = trie[u].next[id];
trie[u].cnt++;
}
}
void getfail(){
int i;
queue<int> Q;
for(i = 0; i < 26; i++)
if(trie[0].next[i])
Q.push(trie[0].next[i]);
while(!Q.empty()){
auto u = Q.front(); Q.pop();
for(i = 0; i < 26; i++){
if(trie[u].next[i]){
Q.push(trie[u].next[i]);
trie[trie[u].next[i]].fail = trie[trie[u].fail].next[i];
}
else
trie[u].next[i] = trie[trie[u].fail].next[i];
}
add_edge(trie[u].fail, u);
}
}
void getsum(int u){
int i;
if(vis[u]) return;
vis[u] = true;
trie[u].sum = trie[u].cnt;
for(i = head[u]; i; i = e[i].next){
int v = e[i].to;
getsum(v), trie[u].sum += trie[v].sum;
}
}
int find(string s){
int i, len = s.size();
int id, u = 0;
for(i = 0; i < len; i++){
id = s[i] - 'a';
u = trie[u].next[id];
}
return trie[u].sum;
}
void add_edge(int x, int y){
e[++cnt_adj].to = y;
e[cnt_adj].next = head[x];
head[x] = cnt_adj;
}