zoukankan      html  css  js  c++  java
  • Keywords Search---hdu2222(AC自动机 模板)

    题目链接http://acm.hdu.edu.cn/showproblem.php?pid=2222

    一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过;

    本题就是最基础的模板;在此之前需要理解kmp和字典树(trie);

    Trie树有3个基本性质:

    (1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符;

    (2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;

    (3) 每个节点的所有子节点包含的字符都不相同。

    1.  AC自动机算法分为3步:                                 

    (1)构造一棵Trie树;  
    (2)构造失败指针;    

    构造失败指针的过程概括起来就一句话:设这个节点上的字母为x,沿着他父亲的失败指针走,

    直到走到一个节点,他的儿子中也有字母为x的节点。然后把当前节点的失败指针指向那个字符

    也为x的儿子。如果一直走到了root都没找到,那就把失败指针指向root。

    1: root的子节点的失败指针都指向root。

    2: 节点(字符为x)的失败指针指向:从X节点的父节点的fail节点回溯直到找到某节点的子节点也是字符x,没有找到就指向root。

    (3)根据AC自动机,搜索待处理的文本。

    从root节点开始,每次根据读入的字符沿着自动机向下移动。 当读入的字符,在分支中不存在时,递归走失败路径。

    如果走失败路径走到了root节点, 则跳过该字符,处理下一个字符。 因为AC自动机是沿着输入文本的最长后缀移

    动的,所以在读取完所有输入文本后,最后递归走失败路径,直到到达根节点, 这样可以检测出所有的模式。

    搜索的步骤:
    1. 从根节点开始一次搜索;                              

    2. 取得要查找关键词的第一个字符,并根据该字符选择对应的子树并转到该子树继续进行检索;

    3. 在相应的子树上,取得要查找关键词的第二个字符,并进一步选择对应的子树进行检索。  

    4. 迭代过程……                                   

    5. 在某个节点处,关键词的所有字符已被取出,则读取附在该节点上的信息,即完成查找。

      匹配模式串中出现的单词。当我们的模式串在Trie上进行匹配时,如果与当前节点的关键字不能继续匹配的时候,

      就应该去当前节点的失败指针所指向的节点继续进行匹配。

    匹配过程出现两种情况:
    1. 当前字符匹配,表示从当前节点沿着树边有一条路径可以到达目标字符, 此时只需沿该路径走向下一个节点继续匹配即可   ,目标字符串指针移向下个字符继续匹配;                                       

    2. 当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配,匹配过程随着指针指向root结束。                                                                   

     重复这2个过程中的任意一个,直到模式串走到结尾为止。

    哎,看了大神的讲解:不会AC自动机基础的可以参考一下:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int N = 1e6+7;
    char MumStr[N];
    struct node
    {
        node *next[26], *fail;
        int leaf;///以该字符结束的个数
    };
    
    void AddTrie(char s[], node *head)///建立Trie树;
    {
        node *p = head;
        for(int i=0; s[i]; i++)
        {
            int k = s[i] - 'a';
    
            if(p->next[k] == NULL)
                p->next[k] = new node();
    
            p = p->next[k];
        }
        p->leaf ++;///防止出现重复单词;
    }
    void GetFail(node *head)
    {
        queue<node*>Q;
        Q.push(head);
        while(Q.size())
        {
            node *p = Q.front();
            Q.pop();
            for(int i=0; i<26; i++)
            {
                if(p->next[i] != NULL)
                {
                    node *t = p->fail;
    ///设这个节点上的字母为x,沿着他父亲的失败指针走,
    ///直到走到一个节点,他的儿子中也有字母为x的节点。
    ///然后把当前节点的失败指针指向那个字符也为x的儿子;
                    while(t != NULL)
    
                    {
                        if(t->next[i] != NULL)
                        {
                            p->next[i]->fail = t->next[i];
                            break;
                        }
                        t = t->fail;
                    }
                    ///如果一直走到了root都没找到,那就把失败指针指向root。
                    if(t == NULL)p->next[i]->fail = head;
                    Q.push(p->next[i]);
                }
            }
        }
    }
    int Query(node *head)
    {
        int sum = 0;
        node *p = head;
        for(int i=0; MumStr[i]; i++)
        {
            int k = MumStr[i] - 'a';
    
            while(p->next[k] == NULL && p != head)///当前字符不匹配;
                p = p->fail;
            if(p->next[k] == NULL)continue;
            p = p->next[k];
            node *t = p;
            while(t!=head && t->leaf != -1)
            {
                sum += t->leaf;
                t->leaf = -1;
                t = t->fail;
            }
        }
        return sum;
    }
    int main()
    {
        int T, n;
        char s[110];
        scanf("%d", &T);
        while(T--)
        {
            node *head = new node();
            scanf("%d", &n);
            for(int i=0; i<n; i++)
            {
                scanf("%s", s);
                AddTrie(s, head);
            }
            GetFail(head);
            scanf("%s", MumStr);
            int ans = Query(head);
            printf("%d
    ", ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    蓝牙低功耗(Bluetooth Low Energy)
    Android 蓝牙(概述)
    Android 学习笔记之 Activity 简介
    Android 学习笔记之常用控件
    Android 学习笔记之界面布局
    委托和事件(C#)
    Java 资源汇总
    如何阅读英文原版教材
    Combobox 控件绑定数据
    《将博客搬至CSDN》
  • 原文地址:https://www.cnblogs.com/zhengguiping--9876/p/4854752.html
Copyright © 2011-2022 走看看