算法:AC自动机

一、概念

  • 多模式串匹配:提供一个主串,多个模式串,在一个主串中找多个模式串,例如在一个文本中过滤多个敏感词就属于这个范畴
  • AC自动机就是一种多模式串匹配算法,原名Aho-Corasick automaton

二、思路

(一) 、构建tire树,将多个模式串放入tire树中,与一般tire树不同的是,每个tire树多存一个模式串的长度和每个节点的失效指针,具体可见代码
(二)、广度优先遍历tire树构建失效指针,何为失效指针,就是主串沿着tire树遍历,当遍历到tire树叶子节点走不下去的时候,就沿着tire树叶子节点的失效指针的指向继续走,如何构建失效指针呢?
1)首先要明白,失效指针的查找过程就是找匹配的最长后缀,如head节点走到下图中she的e中,she的后缀有e,he,那最长就是找he,那图中Hers中就有he,在遍历的过程中怎么知道有个hers,其中就有个he呢?这就要用到父节点
2)e的父节点是h,那sh的最长后缀就是h,sh的h怎么找到hers的h呢,就要用到s,通过h找到s,在通过s找到head,head有一个子节点为h,e的失效指针查找过程同样如此,知道h指向hers的h,判断hers的h有没有e的子节点,如果有,e就指向hers的e,没有就指向头节点
(三)、匹配过程:
1)如模式串是her,his,she,he,主串是mhishers,模式串构建的tire树如下,图中的黑色虚线表示每个节点的失效指针指向,我们来遍历一遍主串
2)首先是m,头节点指向的s、h,没有m,直接跳过
3)然后是h,走tire树s分支,然后是i,走i分支,然后是s,找到第一个单词his了,可以通过节点中的长度把下标打印出来,继续往下走是h,因为s没有节点为s,即这时候就走不下去,这就要用到失效指针
4)看图中指向,s的失效指针是head下s,就沿着head下s继续往下走,依次类推
在这里插入图片描述

三、代码实现

package StringMatch;

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

/*
    AC自动机:多模式串匹配算法
    思路:Tire树+KMP
    单纯的用tire树也可以进行多模式串匹配,把模式串全部放入Tire树中,多次用主串遍历tire树
    而AC自动机只需遍历一遍主串就可完成多模式串匹配,只有在极端情况下每次,失效指针每次都指向头节点,AC自动机的性能才会退化和Tire树一样
 */
public class AcAuto_zhu {
    private static final node head = new node('/');
    static class node{
        private char data;
        private int length = -1;
        private boolean isEndgingChar = false;
        private node[] children = new node[26];
        private node fail;
        public node(char data){
            this.data = data;
        }
    }

    private static void insert(String str){
        char[] text = str.toCharArray();
        node p = head;

        for(int i = 0; i < text.length; i++){
            int index = text[i] - 'a';
            if (p.children[index] == null){
                p.children[index] = new node(text[i]);
            }
            p = p.children[index];
        }
        p.isEndgingChar = true;
        p.length = text.length;
    }

    //广度优先遍历tire树构建失效指针
    private static void buildFail(){
        Queue<node> queue = new LinkedList<>();
        head.fail = null;
        queue.add(head);

        while(!queue.isEmpty()){
            node p = queue.remove();
            for (int i = 0; i < 26; i++){
                node q = p.children[i];
                if (q == null){
                    continue;
                }
                if (p == head){
                    q.fail = head;
                }else{
                    node r = p.fail;
                    while (r != null){
                        node m = r.children[q.data - 'a'];
                        if (m != null){
                            q.fail = m;
                            break;
                        }
                        r = r.fail;
                    }
                    if (r == null){
                        q.fail = head;
                    }
                }
                queue.add(q);
            }
        }
    }

    public static void match(String mainString,String...args){
        char[] main = mainString.toCharArray();
        for (String s : args){
            insert(s);
        }
        buildFail();

        node p = head;
        for(int i = 0; i < main.length; i++){
            int index = main[i] - 'a';
            if (p.children[index] == null && p != head){
                p = p.fail;
            }
            p = p.children[index];
            if (p == null){
                p = head;
            }
            node tmp = p;
            while (tmp != head){
                if (tmp.isEndgingChar == true){
                    int pos = i - tmp.length + 1;
                    System.out.println("下标为:" + pos + "长度为:" + tmp.length);
                }
                tmp = tmp.fail;
            }
        }
    }

    public static void main(String[] args) {
        String main = "ahishers";
        match(main,"hers","his","she","he");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我爱夜来香A

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

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

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

打赏作者

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

抵扣说明:

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

余额充值