zoukankan      html  css  js  c++  java
  • ac自动机

    https://www.bilibili.com/video/av6295004?from=search&seid=4744549383249600114

    还是看视频把。。

    kmp是求一个串和另一个串的匹配

    ac自动机就叼了///。。。emm。。是求多个串和一个串的匹配

    先trie建树  把多个串放到树内

    然后build弄fail指针

    fail的作用就是   匹配到当前结点 如果他没有儿子了  就不能继续匹配了 但如有fail 则还可以

    she  her  这两个单词和  abcsher匹配 问 这两个单词是否出现了

    如果没有fail  我们就要把每个后缀找出来 每次都要检测一下 是否出现了 比如把abcsher  bcsher csher sher her  er  e这些一个个拿出来 是不是很是撒比

    所以。。用了fai后 abcsher 在trie树上走 走到e时单词数cnt++(事先标记单词结尾)  e没有儿子  但e指向了r  所以走到r  单词数cnt++

    可能会有人问会不会出现 没有匹配h  但从e直接走到r了   。。不会出现这种情况的

      因为要让e的fail指向r  必须它的父结点指向r的父结点。。可以看看代码。。就是这么个思路来建的fail

    板子:

    #include <iostream>
    #include <cstdio>
    #include <sstream>
    #include <cstring>
    #include <map>
    #include <set>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <algorithm>
    #include <cmath>
    #define rap(i, a, n) for(int i=a; i<=n; i++)
    #define rep(i, a, n) for(int i=a; i<n; i++)
    #define lap(i, a, n) for(int i=n; i>=a; i--)
    #define lep(i, a, n) for(int i=n; i>a; i--)
    #define MOD 2018
    #define LL long long
    #define ULL unsigned long long
    #define Pair pair<int, int>
    #define mem(a, b) memset(a, b, sizeof(a))
    #define _  ios_base::sync_with_stdio(0),cin.tie(0)
    //freopen("1.txt", "r", stdin);
    using namespace std;
    const int maxn = 1000010, maxm = 500005, INF = 0x7fffffff;
    int tot;
    queue<int> q;
    
    struct state
    {
        int next[26];
        int fail, cnt;
    }trie[500005];
    
    
    void init()
    {
        while(!q.empty()) q.pop();
        for(int i=0; i<maxm; i++)
        {
            mem(trie[i].next, 0);
            trie[i].fail = trie[i].cnt = 0;
        }
        tot = 0;
    }
    
    void insert(char *s)
    {
        int  n = strlen(s);
        int rt = 0;
        for(int i=0;i<n; i++)
        {
            int x = s[i] - 'a';
            if(!trie[rt].next[x])
            {
                trie[rt].next[x] = ++tot;
            }
            rt = trie[rt].next[x];
        }
        trie[rt].cnt++;
    }
    
    void build()
    {
        trie[0].fail= -1;
        q.push(0);
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            for(int i=0; i<26; i++)
            {
                if(trie[u].next[i])
                {
                    if(u == 0) trie[trie[u].next[i]].fail  = 0;  //祖结点的儿子的fail为 祖结点 因为 没有比祖结点还靠前的结点
                    else{
                        int v = trie[u].fail;            //如果不是祖结点的儿子  那就找它爸爸的fail
                        while(v != -1)                //循环直到找到它爸爸的fail或者它爸爸fail的fail······有为i儿子 
                        {
                            if(trie[v].next[i])
                            {
                                trie[trie[u].next[i]].fail = trie[v].next[i];  //那么这个fail的i的儿子   就是当前fail的儿子
                                break;
                            }
                            v = trie[v].fail;
                        }
                        if(v == -1) trie[trie[u].next[i]].fail = 0;    //一直找不到,那就指向祖结点
                    }
                    q.push(trie[u].next[i]);
                }
            }
        }
    }
    
    int Get(int u)
    {
        int res = 0;
        while(u)
        {
            res = res + trie[u].cnt;
            trie[u].cnt = 0;
            u = trie[u].fail;
        }
        return res;
    }
    
    
    
    int math(char *s)
    {
        int n = strlen(s);
        int rt = 0, res = 0;
        for(int i=0; i<n; i++)
        {
            int x = s[i] - 'a';
            if(trie[rt].next[x])    //如果有儿子 就直接下传
                rt = trie[rt].next[x];
            else                    //如果没有  就去fail去找
            {
                int p = trie[rt].fail;
                while(p != -1 && trie[p].next[x] == 0) p = trie[p].fail;
                if(p == -1) rt = 0; //一直没找到 则返回祖结点
                else rt = trie[p].next[x];
            }
            if(trie[rt].cnt)    //如果以这个点为后缀的串还没有被匹配 则匹配
                res = res + Get(rt);
        }
        return res;
    
    }
    
    int T, n;
    char s[maxn];
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            init();
            scanf("%d", &n);
            rap(i, 1, n)
            {
                scanf("%s", s);
                insert(s);
            }
            build();
            scanf("%s", s);
            printf("%d
    ", math(s));
        }
        return 0;
    }
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    【IO流】FileInputStream FileOutputStream BufferInputStream BufferOutputStream
    [集合]集合相关简单总结
    [集合]Map的 entrySet() 详解以及用法(四种遍历map的方式)
    [集合]HashMap和Hashtable区别
    [集合]Map
    [集合Set]HashSet、LinkedHashSet TreeSet
    IOS UIView 之实例方法篇 Instance Methods
    css 让一张彩色的图片变成一张黑白图
    窗口改变的时候自动刷新浏览器
    php 交互式命令行工具
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/9462745.html
Copyright © 2011-2022 走看看