zoukankan      html  css  js  c++  java
  • AC自动机 HDU2222

    学了KMP算法,就能解决大部分的单模匹配,但是当有多个搜索词的时候就捉襟见肘了。然后就又有一个新知识(对我来说),AC自动机。

    之前也听说过,但是看到这个东西排在lrj的书的后面,我就被吓到了,所以还是说不能害怕做题,要敢于正视问题,才能解决问题。

    和KMP类似,AC自动机放在字典树的基础上,增加了类似KMP的next数组的东西,里面叫做失败指针,当然好像kmp那个next也叫失败数组。。。

    原理详解: 点击

    不过我觉的还是要结合题来看比较好理解,还没有用数组的映射实现过,只是用了指针(还写了两遍Orz,学完后还有昨天),有空把数组的学会补上 Mark

    题意:给定字典,多个单词

    再给一个句子,问字典中有多少个单词再句子中出现过。改造可以在字典树的里面增加元素(字符串,字符串长度,标记等等)

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <iostream>
    using namespace std;
    const int MAXN = 1e6 + 5;
    struct Trip
    {
        Trip* next[26];
        Trip* fail;
        int sum;
        Trip()
        {
            memset(next, 0, sizeof(next));
            fail = NULL;
            sum = 0;
        }
    };
    string str;
    Trip *root;
    Trip *q[MAXN];
    int cnt = 0;
    int n,t;
    void init()
    {
        root = new Trip();
    }
    
    void Insert(string s)
    {
    
        Trip *p = root;
        int n = s.size();
        for(int k = 0; k < n; k++)
        {
            int i = s[k] - 'a';
            if(p->next[i] == NULL)
            {
                p->next[i] = new Trip();
            }
            p = p->next[i];
        }
        p->sum ++ ;
    }
    
    void build_fail_pointer()
    {
        int head = 0;
        int tail = 1;
        memset(q, 0, sizeof(q));
        q[head] = root;
    
        while(head < tail)
        {
            Trip *p = q[head++];
            for(int i = 0; i < 26; i++)
            {
                if(p->next[i] != NULL)
                {
                    q[tail++] = p->next[i];
                    if(p == root)
                    {
                        p->next[i]->fail = root;
                    }
                    else
                    {
                        Trip *q = p->fail;
                        if(q->next[i] != NULL)
                        {
                            p->next[i]->fail = q->next[i];
                        }
                        else
                        {
                            while(q)
                            {
                                if(q->next[i] != NULL)
                                {
                                    p->next[i]->fail = q->next[i];
                                    break;
                                }
                                q = q->fail;
                            }
                            if(q == NULL) p->next[i]->fail = root;
                        }
                    }
                }
            }
        }
    }
    
    void ac_solve(string s)
    {
        Trip *p = root;
        int n = s.size();
        for(int i = 0; i < n; i++)
        {
            int k = s[i] - 'a';
            while(p->next[k] == NULL && p != root) p = p->fail;
            p = p->next[k];
            if(p == NULL) p = root;
            Trip* temp = p;
            while(temp != root)
            {
                if(temp->sum >= 0)
                {
                    cnt += temp->sum;
                    temp->sum = -1;
    
                }
                else
                    break;
                temp = temp->fail;
            }
        }
    }
    
    int main()
    {
        std::ios_base::sync_with_stdio(false);
    
        cin>>t;
        while(t--)
        {
            cnt = 0;
            init();
            cin>>n;
            while(n--)
            {
                cin>>str;
                Insert(str);
            }
            build_fail_pointer();
            cin>>str;
            ac_solve(str);
            printf("%d
    ", cnt);
            delete root;
        }
        return 0;
    
    }
    如果有错误,请指出,谢谢
  • 相关阅读:
    Mayan游戏 (codevs 1136)题解
    虫食算 (codevs 1064)题解
    靶形数独 (codevs 1174)题解
    黑白棋游戏 (codevs 2743)题解
    神经网络 (codevs 1088) 题解
    The Rotation Game (POJ 2286) 题解
    倒水问题 (codevs 1226) 题解
    银河英雄传说 (codevs 1540) 题解
    生日蛋糕 (codevs 1710) 题解
    第一章 1.11 高阶函数
  • 原文地址:https://www.cnblogs.com/Alruddy/p/7226194.html
Copyright © 2011-2022 走看看