【AC自动机_每个模式串在文本串中出现的次数】HDU 3065 病毒侵袭持续中

霸气de小男生 提交于 2020-02-07 05:02:06

 HDU 3065 病毒侵袭持续中

  • 题意:给出n个全是英文大写字母的模式串,保证每个模式串不完全相同。然后再给出一个文本串,字符集由包含ASCII可见字符组成。问每个模式串在文本串中出现的次数。

一: 

  •  思路:其实就是一个AC自动机的板子???只是分别统计了每个模式串出现的次数而已???真的是这样!!!!!我真的迷…… 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 50004;
const int maxS = 2000006;
char str[1005][55];
struct AC_automat{
    int trie[maxN][26], tot;
    int fail[maxN];
    int endd[maxS];
    int times[maxN];

    void Clear(int rt)
    {
        for(int i = 0; i < 26; i ++ )
            trie[rt][i] = 0;
        times[rt] = 0;
    }

    void init()
    {
        tot = 0;
        Clear(0);
    }

    void Insert(char *s, int p)
    {
        int rt = 0;
        for(int i = 0; s[i]; i ++ )
        {
            int id = s[i] - 'A';
            if(!trie[rt][id]) { trie[rt][id] = ++ tot; Clear(tot); }
            rt = trie[rt][id];
        }
        endd[p] = rt;
    }

    void build()
    {
        memset(fail, 0, sizeof(fail));
        queue<int>que;
        for(int i = 0; i < 26; i ++ )
            if(trie[0][i]) que.push(trie[0][i]);
        while(!que.empty())
        {
            int rt = que.front(); que.pop();
            for(int i = 0; i < 26; i ++ )
            {
                if(trie[rt][i])
                {
                    fail[trie[rt][i]] = trie[fail[rt]][i];
                    que.push(trie[rt][i]);
                } else trie[rt][i] = trie[fail[rt]][i];
            }
        }
    }

    void query(char *t)
    {
        int now = 0;
        for(int i = 0; t[i]; i ++ )
        {
            if(t[i] >= 'A' && t[i] <= 'Z')
            {
                int id = t[i] - 'A';
                now = trie[now][id];
                for(int nex = now; nex ; nex = fail[nex])
                    ++ times[nex];
            } else now = 0;
        }
    }

    void print(int n)
    {
        for(int i = 0; i < n; ++ i )
        {
            if(times[endd[i]])
                printf("%s: %d\n", str[i], times[endd[i]]);
        }
    }

}ac_auto;
int n;
char s[maxS];
int main()
{
    while(~scanf("%d", &n))
    {
        ac_auto.init();
        for(int i = 0; i < n; i ++ )
        {
            scanf("%s", str[i]);
            ac_auto.Insert(str[i], i);
        }
        ac_auto.build();
        getchar();
        gets(s);
        ac_auto.query(s);
        ac_auto.print(n);
    }
    return 0;
}

 


二:

思路:用Fail树求解

这个和P5357 【模板】AC自动机(二次加强版)大致是一样的,只是文本串的范围大了些。我们遍历的时候直接不管大写字母之外的字符就可。

注意

  1. 可能包含空格,所以用gets读入文本串【好叭是我想多了,空格好像不是ascii码可见字符,scanf也可以的】
  2. 初始化的问题。该题是多组输入!!!洛谷是单组的,所以我构建fail树的时候没有对vector进行初始化,也没关系。然后这道题就WA了,调了下发现是vector没有清空TAT。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxS = 2000006;
const int maxN = 50004 * 26;
char str[1003][55];
struct AC_automat{
    int trie[maxN][26], tot;
    int fail[maxN];
    int times[maxN];
    int endd[maxS];

    void Clear(int rt)
    {
        for(int i = 0; i < 26; ++ i )
            trie[rt][i] = 0;
        times[rt] = 0;
    }

    void init()
    {
        tot = 0;
        Clear(0);
    }

    void Insert(char *s, int pos)
    {
        int rt = 0;
        for(int i = 0; s[i]; ++ i )
        {
            int id = s[i] - 'A';
            if(!trie[rt][id]) { trie[rt][id] = ++ tot; Clear(tot); }
            rt = trie[rt][id];
        }
        endd[pos] = rt;
    }

    void build()
    {
        memset(fail, 0, sizeof(fail));
        queue<int>q;
        for(int i = 0; i < 26; ++ i) if(trie[0][i]) q.push(trie[0][i]);
        while(!q.empty())
        {
            int rt = q.front(); q.pop();
            for(int i = 0; i < 26; ++ i )
            {
                if(trie[rt][i])
                {
                    fail[trie[rt][i]] = trie[fail[rt]][i];
                    q.push(trie[rt][i]);
                } else trie[rt][i] = trie[fail[rt]][i];
            }
        }
    }

    void get_times(char *t)
    {
        int now = 0;
        for(int i = 0; t[i]; ++ i)
        {
            if(t[i] >= 'A' && t[i] <= 'Z')
                ++ times[now = trie[now][t[i] - 'A']];
            else
                now = 0;
        }
    }

    vector<int>vt[maxN];
    void buildFail()
    {
        for(int i = 0; i <= tot; ++ i)
            vt[i].clear();
        for(int i = 1; i <= tot; ++ i)
            vt[fail[i]].push_back(i);
    }

    void dfs(int u)
    {
        int siz = vt[u].size();
        for(int i = 0; i < siz; ++ i )
        {
            int v = vt[u][i];
            dfs(v);
            times[u] += times[v];
        }
    }

    void print(int n)
    {
        for(int i = 0; i < n; ++ i )
        {
            if(times[endd[i]])
                printf("%s: %d\n", str[i], times[endd[i]]);
        }
    }

}ac;

int n;
char s[maxS];

int main()
{
    while(~scanf("%d", &n))
    {
        ac.init();
        for(int i = 0; i < n; ++ i )
        {
            scanf("%s", str[i]);
            ac.Insert(str[i], i);
        }
        getchar();
        ac.build();
        gets(s);
        ac.get_times(s);
        ac.buildFail();
        ac.dfs(0);
        ac.print(n);
    }
    return 0;
}

/*
6
SHE
HER
HE
HIM
HIS
E
SHEHEHEHEHIMHEHERiii...eE
4
AA
AAA
BB
CC
ooxxCC%dAAAoen.. ..END
 */

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!