1. 程式人生 > >字串匹配的三個演算法(KMP+字典樹+AC自動機)

字串匹配的三個演算法(KMP+字典樹+AC自動機)

字串匹配的意思是給一個字串集合,和另一個字串集合,看這兩個集合交集是多少。

(1)若是都只有一個字串,那麼就看其中一個是否包含另外一個(一對一,KMP)

https://blog.csdn.net/fkyyly/article/details/48007965

(2)若是父串集合(比較長的,被當做模板)的有多個,子串(拿去匹配的)只有一個,就是問這個子串是否存在於父串之中(字典樹則是一對多的時候匹配常用演算法)

(3)若是子串父串集合都有多個,那麼就是問交集了(AC自動機就是多對多的匹配或者用HashMap)

1. 一對一KMP

2. 一對多Trie樹

Trie中文名又叫做字典樹,字首樹等,因為其結構獨有的特點,經常被用來統計,排序,和儲存大量的字串,經常見於搜尋提示,輸入法文字關聯等,當輸入一個值,可以自動搜尋出可能的選擇。當沒有完全匹配的結果時,可以返回字首最為相似的可能。

基本性質:

1.根節點不含有字元,其餘各節點有且只有一個字元。

2.根節點到某一節點中經過的節點儲存的值連線起來就是對應的字串。

3.每一個節點所有的子節點的值都不應該相同。

借用一下維基百科上的一張圖片:

//定義字首樹資料結構
class TrieNode {
    TrieNode[] son;// TrieNode[26] in this case 儲存直接的孩子節點
    boolean isEnd;// end of a string.
    char val;// No this field mostly.

    TrieNode() {
        son = new TrieNode[26];//26 letters.CaseInsensitive.只是給了26這個長度,但是裡面具體有多少個元素表示有多少個son
        isEnd = false;
    }
}

public class One2Many {

    /**
     字典樹的Java實現。實現了插入、查詢
     */
    TrieNode root = new TrieNode();

    public void insert(String str) {
        if (str == null || str.length() == 0) {
            return ;
        }
        TrieNode node = root;
        char[] letters=str.toCharArray();
        for (int i = 0, len = str.length(); i < len; i++) {
            int pos = letters[i] - 'a';
            if (node.son[pos] == null) {
                node.son[pos] = new TrieNode();
                node.son[pos].val = letters[i];
            }
            node = node.son[pos];
        }
        node.isEnd = true;//每個單詞的結束isEnd才為true,中間預設為flase
    }

    // search a word in the tree.Complete matching.
    public boolean has(String str) {
        if (str == null || str.length() == 0) {
            return false;
        }
        TrieNode node = root;
        char[] letters=str.toCharArray();
        for (int i = 0, len = str.length(); i < len; i++) {
            int pos = letters[i] - 'a';
            if (node.son[pos] != null) {
                node = node.son[pos];
            } else {
                return false;
            }
        }
        return node.isEnd;//只有走到末尾才返回true,如果是字首也是false
    }

    //Trie樹的根節點用特殊字元(這樣下面就可以26個分支了)
    public static void main(String[] args) {
        One2Many tree = new One2Many();
        //用strs構建Trie樹
        String[] strs={
                "banana",
                "band",
                "bee",
                "absolute",
                "acm",
        };
        for(String str:strs){
            tree.insert(str);
        }
        //判斷一個元素在不在tire中也就是一個元素在不在另外一個集合中
        System.out.println(tree.has("band"));
    }
}

https://blog.csdn.net/u010233260/article/details/45752777

http://bylijinnan.iteye.com/blog/1525203

3. 多對多AC自動機或者Map

給一個字典,再給一個m長的文字(m長的文本里麵包含很多的詞),問這個文本里出現了字典裡的哪些字。

3.1 方法一:使用HashMap複雜度是O(maxLengh(word)*length(str))這樣和字典的大小沒有關係

import java.util.ArrayList;
import java.util.HashMap;

/**
 * 一對多,把多的那個存入hashmap中
 * 應用場景最大正向匹配分詞
 */

public class Many2Many {
    public void matchFun(String str){
        String[] dic = {"請", "播放", "一首", "黎明", "的", "太陽", "黎明的太陽"};
        HashMap<String, String> dicMap = new HashMap<>();
        int maxLength = 0;
        for (int i = 0; i < dic.length; i++) {
            dicMap.put(dic[i], "1");
            maxLength = dic[i].length();
        }
        String temp = "";
        for (int length = maxLength; length > 0 ; length --) {
            for (int i = 0; i < str.length() && i+length < str.length()+1; i++) {
                temp = str.substring(i, i+length);
                if (dicMap.get(temp) != null){
                    System.out.println(temp);
                }
            }
        }
    }

    public static void main(String[] args) {
        One2Many many2Many = new Many2Many();
        Many2Many.matchFun("你好,請播放黎明的太陽");
    }
}

3.2 方法二:AC自動機

如果用AC自動機來解決的話,效率將為線性O(length(str))時間複雜度。

AC自動機也運用了一點KMP演算法的思想。簡述為字典樹+KMP也未為不可。其實就是在trie樹上做KMP。AC自動機增加了失敗轉移,轉移到已經輸入成功的文字的字尾,來實現。

首先講一下acnode的結構:

與字典樹相比,就多了個*fail對吧,這個就相當於KMP演算法裡的next陣列。只不過它存的是失配後跳轉的位置,而不是跳轉之後再向前跳了多少罷了。

3.2.1.多模式匹配

  多模式匹配就是有多個模式串P1,P2,P3...,Pm,求出所有這些模式串在連續文字T1....n中的所有可能出現的位置。

  例如:求出模式集合{"nihao","hao","hs","hsr"}在給定文字"sdmfhsgnshejfgnihaofhsrnihao"中所有可能出現的位置

3.2.2.Aho-Corasick演算法  

  使用Aho-Corasick演算法需要三步:

  1.建立模式的Trie

  2.給Trie新增失敗路徑

  3.根據AC自動機,搜尋待處理的文字

  下面說明這三步:

建立多模式集合的Trie

  Trie樹也是一種自動機。對於多模式集合{"say","she","shr","he","her"},對應的Trie樹如下,其中紅色標記的圈是表示為接收態:

為多模式集合的Trie樹新增失敗路徑,建立AC自動機

AC自動機裡面比較難理解的應該是它的失配指標的計算過程。這個計算過程從本質上講就是進行一遍廣搜,於此同時維護fail指標,每一步的維護過程可用下圖表示。

構造失敗指標的過程概括起來就一句話:設這個節點上的字母為C,沿著他父親的失敗指標走,直到走到一個節點,他的兒子中也有字母為C的節點。然後把當前節點的失敗指標指向那個字母也為C的兒子。如果一直走到了root都沒找到,那就把失敗指標指向root。

  使用廣度優先搜尋BFS,層次遍歷節點來處理,每一個節點的失敗路徑。  

  特殊處理:第二層要特殊處理,將這層中的節點的失敗路徑直接指向父節點(也就是根節點)。

 

https://blog.csdn.net/asdfghjkl1993/article/details/43371951

http://www.cnblogs.com/xudong-bupt/p/3433506.html

https://blog.csdn.net/qq_30346729/article/details/7883504