题目描述
C同学很喜欢唱歌。唱了n首歌后他发现有一些歌词在这n首歌中经常出现。比如:
《两只蝴蝶》
亲爱的你慢慢飞
小心前面带刺的玫瑰
亲爱的你张张嘴
风中花香会让你沉醉
……
《风雨彩虹铿锵玫瑰》
一切美好只是昨日沉醉
淡淡苦涩才是今天滋味
想想明天又是雨晒风吹
再苦再累无惧无畏
身上的痛让我难以入睡
脚下的路还有更多的累
追逐梦想总是百转千回
无怨无悔从容面对
风雨彩虹 铿锵玫瑰
再多忧伤再多痛苦自己去背
风雨彩虹 铿锵玫瑰
……
在上述两首歌的片段中,玫瑰总共出现了3次,风总共出现了4次。
C同学列出了w个他认为经常出现的歌词,他想知道每个歌词在这n首歌中出现了多少次。
输入格式
第一行一个整数w。
接下来w行,每行一个字符串,第i行表示第i个歌词。
接下来一个整数n。
接下来n行,每行一个字符串,第i行表示第i首歌。
字符串由大小写字母,数字与”-“组成。
输出格式
一共w行,每行一个整数,第i行表示第i个歌词在这n首歌中出现了多少次。
数据范围
输入样例
【样例一输入】
5
he
she
sher
his
hers
2
ushers
she-said-he-said-she-said-he-said-his
【样例二输入】
3
who
shawty
hawt
2
Get-it-shawty-Get-it-shawty
Whoa-W-W-Whoa-Shawtyyyyy
样例三输入:
1
aa
1
aaa
输出样例
【样例一输出】
5
3
1
1
1
【样例二输出】
0
2
3
样例三输出:
2
题解
本题就是一道AC自动机的果题。然而我太久太久没码过AC机(到是天天去),做的时候码了半天,一运行立刻停止工作,我掏出以前的模板,发现自己打错了一些小细节。最后千辛万苦过了样例,交上去只有90分。原来这题歌词可能重复出现,于是我们就再开个数组记一下就行了。
吹B吹了好久,忘了讲做法了。就是对短串建AC机,然后用长串在上面跑,经过一个点匹配的话,就代表沿着fail指针一路跳都是匹配的(都是后缀),于是我们只要建一棵fail树,由fail指针向自己连边,最后dfs一边fail树,将权值和向上加就可以了。这样是严格线性的。然而我写的代码是每次慵懒地向上沿fail指针匹配,这样理论最坏可能到二次方级别,不过数据是没有那样刁钻的。
最后,此题空间512MB,AC机理论上可能开到250w,但是实际比这要小得多,所以能开多少是多少,最后A了就好。KsCla还有超级厉害的SAM作法,但是本蒟蒻还是不会SAM,又没时间去学(要是可以不上文化课就有时间了),所以SAM这个坑就以后再填吧。
代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXL 50002
#define MAXN 502
#define MAXM 1000000
using namespace std;
int w, n, cnt, Ans[MAXN], len, p, name[MAXN];
char s[MAXL];
struct AC{
AC *son[63], *fail;
int id;
void Clear(){
id = 0;
for(int i = 0; i < 63; i++)
son[i] = NULL;
}
}Node[MAXM], *q[MAXM], *Root, *now, *temp;
AC *NewTnode(){
Node[cnt].Clear();
return Node+cnt++;
}
int Get_pos(char x){
if(x >= 'a' && x <= 'z') return x - 'a';
if(x >= 'A' && x <= 'Z') return x - 'A' + 26;
if(x >= '0' && x <= '9') return x - '0' + 52;
return 62;
}
void AC_Insert(char *x, int id){
now = Root;
len = strlen(x);
for(int i = 0; i < len; i++){
p = Get_pos(x[i]);
if(!now->son[p]) now->son[p] = NewTnode();
now = now->son[p];
}
name[id] = id;
if(now->id) name[id] = now->id;
else now->id = id;
}
void AC_Build(){
int head, tail;
q[head = tail = 0] = Root;
Root->fail = NULL;
while(head <= tail){
now = q[head++];
for(int i = 0; i < 63; i++) if(now->son[i]){
q[++tail] = now->son[i];
now->son[i]->fail = Root;
temp = now->fail;
while(temp){
if(temp->son[i]){
now->son[i]->fail = temp->son[i];//Saber
break;
}
temp = temp->fail;
}
}
}
}
void AC_Find(char *x){
now = Root;
len = strlen(x);
for(int i = 0; i < len; i++){
p = Get_pos(x[i]);
while(!now->son[p] && now != Root) now = now->fail;//Saber
if(!now->son[p]) continue;//Saber
now = now->son[p];//Saber
temp = now;
while(temp){
Ans[name[temp->id]] ++;
temp = temp->fail;
}
}
}
int main(){
Root = NewTnode();
scanf("%d", &w);
for(int i = 1; i <= w; i++){
scanf("%s", &s);
AC_Insert(s, i);
}
AC_Build();
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%s", &s);
AC_Find(s);
}
for(int i = 1; i <= w; i++)
printf("%d\n", Ans[name[i]]);
return 0;
}
山有木兮木有枝,心悦君兮君不知。