歌词(AC自动机)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了歌词(AC自动机)相关的知识,希望对你有一定的参考价值。

题目描述

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;
}

 































































以上是关于歌词(AC自动机)的主要内容,如果未能解决你的问题,请参考以下文章

HDU3247 Resource Archiver(AC自动机+BFS+DP)

POJ3691DNA repair(AC自动机,DP)

HDU4057 Rescue the Rabbit(AC自动机+状压DP)

Codeforces 86C Genetic engineering(AC自动机+DP)

POJ1699 Best Sequence(AC自动机+状压DP)

机器学习PAI为你自动写歌词,妈妈再也不用担心我的freestyle了(提供数据代码)