33.1:AC自动机

package algorithmbasic.basicsets.class33;

import java.util.LinkedList;
import java.util.Queue;

public class AC {
    /** AC自动机 -> 给我一个大文章,我可以迅速的找到给定关键词的次数 **/


    /**
     * 思路
     * 1: 根据给出的关键词创建前缀树
     * 2: 广度优先遍历构建fail指针
     * 3:遍历大文章来计算关键词是否出现
     */

    public static class Node {
        public Node[] nexts;
        Node fail;
        int end;//多少个字符串以当前节点为结尾

        public Node() {
            nexts = new Node[26];
            fail = null;
            end = 0;
        }
    }

    public static class ACAutomation {
        //  搭建正确的容器
        //1 根据关键词创建前缀树
        //2 广度优先遍历连接每一个Node节点的fail指针

        //  遍历大文章来计算有多少关键字出现

        public static Node root;//根节点

        /**
         * 1:根据给出的关键字创建前缀树
         **/
        public static void insert(String s) {
            char[] str = s.toCharArray();
            Node cur = root;
            for (int i = 0; i < str.length; i++) {
                int index = str[i] - 'a';
                //有当前的路径,就走当前的路径
                if (cur.nexts[index] != null) {
                    cur = cur.nexts[index];
                    continue;
                }
                //没有当前的路径,就创建当前的路径
                cur.nexts[index] = new Node();
                cur = cur.nexts[index];
            }
            //以当前字母结尾的关键字字数加1
            cur.end++;
        }

        /**
         * 2: 广度优先遍历构建fail指针
         **/
        public static void build() {
            //广度优先遍历的神奇
            Queue<Node> queue = new LinkedList<>();
            root.fail = null;
            queue.add(root);
            while (!queue.isEmpty()) {
                Node cur = queue.poll();
                for (int i = 0; i < 26; i++) {
                    if (cur.nexts[i] != null) {
                        Node cfail = cur.fail;
                        //处理当前节点的fail指针
                        //处理方法 - 父亲节点的fail指针指向的的节点有无对应的路径
                        //- 如果没有继续沿着fail指针找,直到回到root的fail指向null
                        cur.nexts[i].fail = root;
                        while (cfail != null) {
                            if (cfail.nexts[i] != null) {
                                cur.nexts[i].fail = cfail.nexts[i];
                                break;
                            }
                            cfail = cfail.fail;
                        }
                        queue.add(cur.nexts[i]);
                    }
                }
            }
        }

        //遍历大文章来计算有多少关键字出现
        public int containNum(String content) {
            char[] str = content.toCharArray();
            int index = 0;
            Node cur = root;
            Node follow = null;
            int res = 0;

            for (int i = 0; i < str.length; i++) {
                index = str[i] - 'a';
                //当前节点是非根节点 与此同时 当前节点没有匹配成功
                while (cur.nexts[index] == null && cur != root) {
                    cur = cur.fail;
                }
                //当前节点匹配成功 || 当前节点是根节点
                //如果说当前节点匹配成功的话 --> 直接进入下一个节点 --> 然后转一圈 --> 转一圈的目的是 -> 寻找尽量多的匹配串
                cur = cur.nexts[index] != null ? cur.nexts[index] : root;
                follow = cur;
                while (follow != root) { //转了一圈回到了root说明该统计的都统计完了,或者说压根没有匹配的
                    if (follow.end == -1) {
                        break;
                    }
                    if (follow.end != 0) {
                        res += follow.end;
                        follow.end = -1;
                    }
                    follow = follow.fail;
                }
            }
            return res;
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HackerTerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值