欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

HDU-2222 Keywords Search 【题解】

程序员文章站 2022-05-05 07:53:12
题目链接:HDU-2222 或 Vjudge 简单说明: 题意是按行给出n个串,再给一个文本,问在文本中出现了串中的几个。题目没强调串是互不相同的哦! ac自动机的建立,其中插入过程借助了字典树,处理回溯数组(也有人称失败数组)过程是一个广搜运用了STL的队列(queue)。ac自动机的过程还在写。 ......

题目链接:  或 

简单说明:

  题意是按行给出n个串,再给一个文本,问在文本中出现了串中的几个。题目没强调串是互不相同的哦!

  ac自动机的建立,其中插入过程借助了字典树,处理回溯数组(也有人称失败数组)过程是一个广搜运用了STL的队列(queue)。ac自动机的过程还在写。

  这是学习ac自动机的第一题,如果wa的话,那就要注意,字串结束标记是如何标记的了。它不能单单 记录,还要 计数。WA了好几次……

my codes:

HDU-2222 Keywords Search 【题解】
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#define set(a) memset(a,0,sizeof(a))
#define set_b(a) memset(a,false,sizeof(a))
using namespace std;
const int M=5e5+10;
const int N=1e6+10;

int tree[M][26];/**< tree[u][i]  表示节点u的i儿子的节点值 */
int ed[M];/**< 字串结束标记,运用比较灵活,本题要计数 */
int nxt[M];/**< 回溯数组 */
char s[N];/**< 文本串 */
int tot;/**< 节点数 */


struct ac_auto
{
    void pre_set()/**< 初始化 */
    {
        tot=1;
        set(ed);
        set(tree);
        set(nxt);
    }
    void build(char str[])/**< 基本插入操作 */
    {
        int len=strlen(str);
        int u=1;
        for(int i=0; i<len; i++)
        {
            int v=str[i]-'a';
            if(!tree[u][v])
                tree[u][v]=++tot;
            u=tree[u][v];
        }
//    printf("%d\n",u);
        ed[u]++;
    }
    void deal_next()/**< 处理回溯数组,用了一个虚拟的零号节点,下面两行表示它的所有儿子与一号建边 */
    {
        for(int i=0; i<26; i++)
            tree[0][i]=1;
        queue<int> q;
        while(!q.empty())
            q.pop();
        nxt[1]=0;/**< 一号节点回溯到零号 */
        q.push(1);/**< 用到了零号节点,先压入 1 。如果不设零号的话,是压入 1 的所有儿子。两种,注意区别 */
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=0; i<=25; i++)
            {
                if(!tree[u][i])/**< u没有i这个儿子 */

                    tree[u][i]=tree[nxt[u]][i];
                                                  /** \brief u 与它的i 儿子之间建边。有了这句话那么每个节点都将
                                                            有26个儿子,就不会存在 某节点没有 i 儿子还要继续找
                                                            父亲的回溯节点的信息这一麻烦。
                                                      \return
                                                    */
                else
                {
                    nxt[tree[u][i]]=tree[nxt[u]][i];/**< u的i儿子nxt值是next[u]的i儿子的号码 */
//                printf("nxt[%d]=%d\n",tree[u][i],tree[nxt[u]][i]);
                    q.push(tree[u][i]);
                }
//            printf("%d 的 %c 儿子编号:%d\n",u,'a'+i,tree[u][i]);
            }
        }
    }
    int search()
    {
        int len=strlen(s);
        int ans=0;
        int u=1,v;
        for(int i=0; i<len; i++)
        {
            v=s[i]-'a';
            u=tree[u][v];
            int tp=u;
            while(tp>1&&ed[tp])
            {
                ans+=ed[tp];
                ed[tp]=0;
                tp=nxt[tp];
            }
        }
        return ans;
    }
};

int main()
{
    int T,n;
    cin>>T;
    while(T--)
    {
        ac_auto().pre_set();
        cin>>n;
        while(n--)
        {
            char str[55];
            scanf("%s",str);
            ac_auto().build(str);
        }
        scanf("%s",s);
        ac_auto().deal_next();
        int ans=ac_auto().search();
        cout<<ans<<endl;
    }
    return 0;
}
View Code