HDU-2222 Keywords Search 【题解】

题目链接:HDU-2222  或  Vjudge

简单说明:

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

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

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

my codes:

#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

 

posted @ 2018-07-30 12:25  yooonn  阅读(443)  评论(0编辑  收藏  举报