题目大意:给出一个由几个单词组成的文章,问每一个单词在文章中出现过多少次。
思路:应该是后缀数组把,但是我还不会,先用AC自动机水过去,但是发现一点都不水。。几乎调了一下午。
首先是用所有出现过的字符串构建一个AC自动机,然后要利用到刚才宽搜时候留下的数组中的宽搜序,按照这个宽搜序整理一下每个节点的cnt,然后最后按照顺序输出。
对于像我这种除了半平面交以外根本没手写过队列的弱渣来说,手写队列简直就是对我的摧残。。。
CODE:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1000010
#define P(a) ((a) - 'a')
using namespace std;
struct Trie;
Trie *pointer[210];
struct Trie{
Trie *son[27],*fail;
int cnt;
Trie() {
cnt = 0;
memset(son,NULL,sizeof(son));
}
void Insert(char *s,int p) {
Trie *now = this;
while(*s != '\0') {
if(now->son[P(*s)] == NULL)
now->son[P(*s)] = new Trie();
now = now->son[P(*s)];
++s,++now->cnt;
}
pointer[p] = now;
}
};
struct AC_Automation{
Trie *root;
AC_Automation() {
root = new Trie();
root->fail = NULL;
}
void Build(int cnt) {
static char s[MAX];
for(int i = 1; i <= cnt; ++i) {
scanf("%s",s);
root->Insert(s,i);
}
static Trie *q[MAX];
int r = 0,h = 0;
for(int i = 0 ; i < 26; ++i)
if(root->son[i] != NULL) {
root->son[i]->fail = root;
q[++r] = root->son[i];
}
while(r != h) {
Trie *now = q[++h];
for(int i = 0; i < 26; ++i)
if(now->son[i] != NULL) {
Trie *temp = now->fail;
while(temp != root && temp->son[i] == NULL)
temp = temp->fail;
if(temp->son[i] != NULL)
temp = temp->son[i];
now->son[i]->fail = temp;
q[++r] = now->son[i];
}
}
while(r) {
q[r]->fail->cnt += q[r]->cnt;
--r;
}
}
}solver;
int points;
int main()
{
cin >> points;
solver.Build(points);
for(int i = 1; i <= points; ++i)
printf("%d\n",pointer[i]->cnt);
return 0;
}