您现在的位置是:课程

数据结构与算法之精妙的AC自动机【料视出品】

2023-06-30 22:21课程 人已围观


 

 

一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一。

简单的说,KMP用来在一篇文章中匹配一个模式串;但如果有多个模式串,需要在一篇文章中把出现过的模式串都匹配出来,就需要Aho-Corasick automaton
算法了。

在KMP算法中,匹配单个字符的时候,我们只需要按照文本线性的扫一遍,然后中途失配的时候,next数组会引导k回溯到正确的位置进行下一步的匹配。

但是多个模式串的时候要怎么匹配呢?Trie树不就是一个多模式的匹配吗,如果我们将KMP和Trie数结合起来,是不是会有意想不到的效果呢?

有了这些思考,AC自动机算法就这样产生了。

在AC自动机中,我们首先将每一个模式串插入到Trie树中去,建立一棵Trie树,然后构建fail指针,fail指针,顾名思义,就是当匹配失败的时候,用来引导k回溯的一个插穿在Trie树的各个节点之间的一些指针,就和KMP算法中的next数组是一样的道理。

下面贴出完整代码:
public class AhoCorasickAutomaton {
    private Node root;
    private List<String> target;
    private  HashMap<String, List<Integer>> result;
    private static class Node{
        String str;
        Node[] table = new Node[128];
        Node fail;
    }
    public AhoCorasickAutomaton() {
        root = new Node();
    }
    private void buildTrietree(List<String> target){
        this.target = target;
        for (String str:target) {
            Node curr = root;
            for (int i = 0; i < str.length(); i++) {
                char ch = str.charAt(i);
                if(curr.table[ch] == null) curr.table[ch] = new Node();
                curr = curr.table[ch];
            }
            curr.str = str;
        }
    }

    private void buildAC(List<String> target){
        buildTrietree(target);
        LinkedList<Node> queue = new LinkedList<Node>();
        for(Node node:root.table){
            if(node != null){
                node.fail = root;
                queue.addLast(node);
            }
        }
        while (!queue.isEmpty()){
            Node parent = queue.poll();
            for(int i = 0;i <parent.table.length ;i++){
                if(parent.table[i] != null){
                    queue.addLast(parent.table[i]);
                    Node failNode = parent.fail;
                    for(;;){
 
                        if(failNode == null){
                            break;
                        }
                        if(failNode.table[i] != null){
                            parent.table[i].fail = failNode.table[i];
                            break;
                        }else failNode = failNode.fail;
                    }
                }
            }
        }
    }
    private HashMap<String ,List<Integer>> findTarget(String text){
        HashMap<String, List<Integer>> result = new LinkedHashMap<String, List<Integer>>();
        for(String str:target){
            result.put(str, new LinkedList<Integer>());
        }
        Node curr = root;
        int i = 0;
        while (i<text.length()){
            char ch = text.charAt(i);
            if(curr.table[ch] != null){
                curr = curr.table[ch];
                if(curr.str != null)result.get(curr.str).add(i - curr.str.length() +1);
                if(curr.fail != null && curr.fail.str != null)
                    result.get(curr.fail.str).add(i - curr.fail.str.length() + 1);
                i++;
            }else{
               curr = curr.fail;
               if(curr == null){
                   curr = root;
                   i++;
               }
            }

        }
        return result;
    }
    public static void main(String[] args) {
        List<String> target = new ArrayList<String>();
        target.add("abcdef");
        target.add("abhab");
        target.add("bcd");
        target.add("cde");
        target.add("cdfkcdf");        
        String text = "bcabcdebcedfabcdefababkabhabk";
        AhoCorasickAutomaton aca = new AhoCorasickAutomaton();
        aca.build_AC_FromTrie(target);
        HashMap<String, List<Integer>> result = aca.findTarget(text);
        for (Entry<String, List<Integer>> entry : result.entrySet()) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
}

 

 

-->

站点信息

  • 文章统计篇文章