题目描述
C同学很喜欢唱歌。唱了n首歌后他发现有一些歌词在这n首歌中经常出现。比如:
《两只蝴蝶》
亲爱的你慢慢飞
小心前面带刺的玫瑰
亲爱的你张张嘴
风中花香会让你沉醉
……
《风雨彩虹铿锵玫瑰》
一切美好只是昨日沉醉
淡淡苦涩才是今天滋味
想想明天又是雨晒风吹
再苦再累无惧无畏
身上的痛让我难以入睡
脚下的路还有更多的累
追逐梦想总是百转千回
无怨无悔从容面对
风雨彩虹 铿锵玫瑰
再多忧伤再多痛苦自己去背
风雨彩虹 铿锵玫瑰
……
在上述两首歌的片段中,玫瑰总共出现了3次,风总共出现了4次。
C同学列出了w个他认为经常出现的歌词,他想知道每个歌词在这n首歌中出现了多少次。
ps:歌词可能会有重复的。
输入格式
第一行一个整数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自动机神犇帮我自动AC……
题解:一道裸的AC自动机
我们把所给的歌词构建一个AC自动机,然后每一首歌都去与AC自动机匹配。
在本题需要注意的是:假如某一个字符能匹配上,那么就一直往回找该字符的fail节点,并判断是否出现以fail(包括当前刚刚匹配的字符)节点为结尾的单词。
附:AC自动机以前也只是看过文章,做的题少,所以写起来漏洞百出,调了好久。
#include<algorithm> #include<fstream> #include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; int w,n,len,cur,pt[1010],ans[550]; char c[50050]; struct tedge { int n,num; tedge *nex[65],*f; }tree[500050]; tedge *Root = tree+0; tedge *d[500050]; int Change(char q) { if (q>='0'&&q<='9') return q-48; if (q>='a'&&q<='z') return q-87; if (q>='A'&&q<='Z') return q-29; return 62; } tedge *NewNode() { cur++; return tree+cur; } void Insert(tedge *root,int wei,int num) { if (wei>len) { if (root->n==0) { root->n = 1; root->num = num; } else pt[num] = root->num; return; } if (root->nex[Change(c[wei])]==NULL) { tedge *y = NewNode(); root->nex[Change(c[wei])] = y; Insert(y,wei+1,num); } else Insert(root->nex[Change(c[wei])],wei+1,num); } void Make_fail() { int h,t; h = t = 1; d[1] = tree+0; while (h<=t) { tedge *fro = d[h]; for (int i=0; i<=62; i++) { if (fro->nex[i]==NULL) continue; t++; d[t] = fro->nex[i]; tedge *fail = fro->f; while (fail!=NULL) { if (fail->nex[i]!=NULL) { fro->nex[i]->f = fail->nex[i]; break; } fail = fail->f; } if (fro->nex[i]->f==NULL) fro->nex[i]->f = Root; } h++; } return; } void Compare() { tedge *p1 = Root; int p2 = 0; while (p2<=len) { if (p1->nex[Change(c[p2])]!=NULL) { p1 = p1->nex[Change(c[p2])]; p2++; tedge *p3 = p1; while (p3!=Root&&p3!=NULL) { if (p3->n==1) ans[p3->num]++; p3 = p3->f; } } else { p1 = p1->f; if (p1==NULL) p1 = Root; if (p1==Root&&p1->nex[Change(c[p2])]==NULL) p2++; } } } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",&w); for (int i=1; i<=w; i++) { scanf("%s",&c); len = strlen(c)-1; Insert(Root,0,i); } tree[0].f = NULL; Make_fail(); scanf("%d",&n); for (int i=1; i<=n; i++) { scanf("%s",&c); len = strlen(c)-1; Compare(); } for (int i=1; i<=w; i++) if (pt[i]!=0) printf("%d\n",ans[pt[i]]); else printf("%d\n",ans[i]); return 0; }