給一個字符串S和一個字符串數組T(T中的字符串要比S短許多),設計一個算法, 在字符串S中查找T中的字符串

給一個字符串S和一個字符串數組T(T中的字符串要比S短許多),設計一個算法, 在字符串S中查找T中的字符串。java

解答

字符串的多模式匹配問題。算法

咱們把S稱爲目標串,T中的字符串稱爲模式串。設目標串S的長度爲m,模式串的平均長度爲 n,共有k個模式串。若是咱們用KMP算法(或BM算法)去處理每一個模式串, 判斷模式串是否在目標串中出現, 匹配一個模式串和目標串的時間爲O(m+n),因此總時間複雜度爲:O(k(m+n))。 通常實際應用中,目標串每每是一段文本,一篇文章,甚至是一個基因庫, 而模式串則是一些較短的字符串,也就是m通常要遠大於n。 這時候若是咱們要匹配的模式串很是多(即k很是大),那麼咱們使用上述算法就會很是慢。 這也是爲何KMP或BM通常只用於單模式匹配,而不用於多模式匹配。數組

那麼有哪些算法能夠解決多模式匹配問題呢?貌似還挺多的,Trie樹,AC自動機,WM算法, 後綴樹等等。咱們先從簡單的Trie樹入手來解決這個問題。spa

Trie樹,又稱爲字典樹,單詞查找樹或前綴樹,是一種用於快速檢索的多叉樹結構。 好比英文字母的字典樹是一個26叉樹,數字的字典樹是一個10叉樹。.net

Trie樹能夠利用字符串的公共前綴來節約存儲空間,這也是爲何它被叫前綴樹。設計

若是咱們有如下單詞:abc, abcd, abd, b, bcd, efg, hig, 能夠構造以下Trie樹: (最右邊的最後一條邊少了一個字母)code

 

回到咱們的題目,如今要在字符串S中查找T中的字符串是否出現(或查找它們出現的位置), 這要怎麼和Trie扯上關係呢?ip

假設字符串S = 「abcd",那麼它的全部後綴是:rem

abcd
bcd
cd
d

咱們發現,若是一個串t是S的子串,那麼t必定是S某個後綴的前綴。好比t = bc, 那麼它是後綴bcd的前綴;又好比說t = c,那麼它是後綴cd的前綴。字符串

所以,咱們只須要將字符串S的全部後綴構成一棵Trie樹(後綴Trie), 而後查詢模式串是否在該Trie樹中出現便可。若是模式串t的長度爲n, 那麼咱們從根結點向下匹配,能夠用O(n)的時間得出t是否爲S的子串。

下圖是BANANAS的後綴Trie:

 

後綴Trie的查找效率很優秀,若是你要查找一個長度爲n的字符串,只須要O(n)的時間, 比較次數就是字符串的長度,至關給力。 可是,構造字符串S的後綴Trie卻須要O(m2 )的時間, (m爲S的長度),及O(m2 )的空間。

 

 

在CODE上查看代碼片派生到個人代碼片

  1. package Hard;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5.   
  6.   
  7. /** 
  8.  * Given a string s and an array of smaller strings T, design a method to search s for each small string in T. 
  9.  
  10. 譯文: 
  11.  
  12. 給一個字符串S和一個字符串數組T(T中的字符串要比S短許多),設計一個算法, 在字符串S中查找T中的字符串。 
  13.  * 
  14.  */  
  15. public class S18_8 {  
  16.   
  17.     // 後綴樹節點  
  18.     static class SuffixTreeNode {  
  19.         HashMap<Character, SuffixTreeNode> children = new HashMap<Character, SuffixTreeNode>();  
  20.   
  21.         char value;  
  22.         ArrayList<Integer> indexes = new ArrayList<Integer>();  
  23.   
  24.         public SuffixTreeNode() {  
  25.         }  
  26.   
  27.         public void insertString(String s, int index) {  
  28.             indexes.add(index);  
  29.             if (s != null && s.length() > 0) {  
  30.                 value = s.charAt(0);  
  31.                 SuffixTreeNode child = null;  
  32.                 if (children.containsKey(value)) {  
  33.                     child = children.get(value);  
  34.                 } else {  
  35.                     child = new SuffixTreeNode();  
  36.                     children.put(value, child);  
  37.                 }  
  38.                 String remainder = s.substring(1);  
  39.                 child.insertString(remainder, index);  
  40.             }  
  41.         }  
  42.   
  43.         public ArrayList<Integer> search(String s) {  
  44.             if (s == null || s.length() == 0) {  
  45.                 return indexes;  
  46.             } else {  
  47.                 char first = s.charAt(0);  
  48.                 if (children.containsKey(first)) {  
  49.                     String remainder = s.substring(1);  
  50.                     return children.get(first).search(remainder);  
  51.                 }  
  52.             }  
  53.             return null;  
  54.         }  
  55.     }  
  56.   
  57.     // 後綴樹  
  58.     static class SuffixTree {  
  59.         SuffixTreeNode root = new SuffixTreeNode();  
  60.   
  61.         public SuffixTree(String s) {  
  62.             for (int i = 0; i < s.length(); i++) {  
  63.                 String suffix = s.substring(i);  
  64.                 root.insertString(suffix, i);  
  65.             }  
  66.         }  
  67.   
  68.         public ArrayList<Integer> search(String s) {  
  69.             return root.search(s);  
  70.         }  
  71.     }  
  72.   
  73.     public static void main(String[] args) {  
  74.         String testString = "mississippi";  
  75.         String[] stringList = { "is", "sip", "hi", "sis" };  
  76.         SuffixTree tree = new SuffixTree(testString);  
  77.         for (String s : stringList) {  
  78.             ArrayList<Integer> list = tree.search(s);  
  79.             if (list != null) {  
  80.                 System.out.println(s + ": " + list.toString());  
  81.             }  
  82.         }  
  83.     }  
  84.   
  85. }  
相關文章
相關標籤/搜索