avatar
fireworks99
keep hungry keep foolish

SDNUOJ 1118 单词统计(AC自动机)

Description

给定一个字符串和若干个单词,统计这些单词在这个字符串中出现的次数。

题目链接 : http://www.acmicpc.sdnu.edu.cn/problem/show/1118

Sample Input1

5
a
ab
ba
aba
bab
aababa

Sample Output1

4 2 2 2 1

Sample Input2(可能卡的样例)

3
aa
aa
aa
aaa

Sample Output2

2 2 2

AC自动机

巧妙的数据结构(Trie) + 灵活的算法(KMP) = 巧妙灵活的算法(AC自动机)

真的是,二者结合地天衣无缝

By the way

Trie里有个sum[N]数组是统计输入(Input)里单词重复出现次数的

此题询问最后给出的长字符串中输入(Input)的每个字符串出现次数的

好像说的不清楚,反正知道有那么两个东西不是一回事就行

Analyze

此题要在主串里查询每个输入串(可能重复)出现次数

  1. 用上Trie中的is[N]数组标记每个字符串结束处结点

  2. 开一个fro[N]数组记录答案(每个字符串出现次数)

    最后遍历结点数(cnt),剔除(is[i] == 0)的fro[i],输出剩下的(本来就是有序的)

    这个时候开始WA了,因为没处理像上方Input2那样的情况

  3. 我想了一个办法(我竟然能在一个较陌生的算法上搞小动作!!!):

    开一个amount[n]数组,记录每个输入字符串尾字母后的结束结点

    最后只需要遍历输入串数(amo)一个个输出:

for(int i = 0; i < amo; ++i)
            printf("%d%c", fro[ amount[i] ], i == amo - 1 ? '\n' : ' ');

Code

#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N =  5 * 1e5 + 9;

bool is[N];
int cnt = 0, amo = 0, fail[N], fro[N], trie[N][26];
int amount[10005];

void clean(int num)
{
    fail[num] = 0, fro[num] = 0;
    memset(trie[num], 0, sizeof(trie[num]));
}

void plug(char * s)
{
    int root = 0;
    for(int i = 0; s[i]; ++i)
    {
        int x = s[i] - 'a';
        if(!trie[root][x])
        {
            ++cnt;
            clean(cnt);
            trie[root][x] = cnt;
        }
        root = trie[root][x];
    }
    is[root] = 1;
///    sum[root]++;统计单词在树中出现次数
    amount[amo++] = root;
}

void getfail()
{
    queue<int> q;
    for(int i = 0; i < 26; ++i)
        if(trie[0][i])
            fail[ trie[0][i] ] = 0, q.push(trie[0][i]);

    while(!q.empty())
    {
        int now = q.front();
        q.pop();

        for(int i = 0; i < 26; ++i)
        {
            if(trie[now][i])
            {
                fail[ trie[now][i] ] = trie[ fail[now] ][i];
                q.push(trie[now][i]);
            }
            else
                trie[now][i] = trie[ fail[now] ][i];
        }
    }
}

void query(char * s)
{
    int now = 0, len = strlen(s);
    for(int i = 0; i < len; ++i)
    {
        now = trie[now][ s[i] - 'a' ];
        for(int j = now; j ; j = fail[j])
            if(is[j])
                fro[j]++;
    }
}

char s[1000005];

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        cnt = 0, amo = 0;
        memset(is, 0, sizeof(is));
        memset(amount, 0, sizeof(amount));
        clean(0);
        for(int i = 0; i < n; ++i)
        {
            scanf("%s", s);
            plug(s);
        }
        getfail();
        scanf("%s", s);
        query(s);
        for(int i = 0; i < amo; ++i)
            printf("%d%c", fro[ amount[i] ], i == amo - 1 ? '\n' : ' ');
    }
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy