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;
    
    }
    如果有错误,请指出,谢谢
  • 相关阅读:
    shape与reshape
    opencv4.5.0 +contrib编译流程
    人脸定位(haar特征)
    最近邻分类法
    人脸识别概述
    跟踪视频中的物体
    估算稠密光流
    resize函数
    swap函数
    hibernate的session执行增删改查方法的执行步骤
  • 原文地址:https://www.cnblogs.com/Alruddy/p/7226194.html
Copyright © 2011-2022 走看看