2009-11-19 59 views
29

我想要一個可以在Java中用於搜索字符串中的子字符串的有效算法(或庫)。用於在字符串中搜索子字符串的快速算法

我想要做的是:

給定的輸入字符串 - INSTR

「BCDEFGH」

而且一組候選串 - CAND

「AB」, 「CDE」, 「FG」, 「H」, 「IJ」

找到任何CAND匹配的子字符串INSTR

中在這個例子中字符串我會匹配「CDE」,「FG」和「H」(但不是「AB」和「IJ」)

可能有很多候選字符串(在CAND中),但更重要的是我將執行此搜索數百萬次,所以我需要它快速。我想用char數組。另外,我並沒有將其構建爲解決方案,比如分發搜索 - 只是本地最有效的功能/算法。

此外,CAND和INSTR中的所有字符串都將相對較小(即字符數爲<),即目標字符串INSTR相對候選字符串不長。


更新我應該提到,集合CAND字符串是跨INSTR的所有值不變。

更新我只需要知道有一場比賽 - 我不需要知道比賽是什麼。

最終更新 我選擇嘗試AhoCorsick和拉賓卡爾普,由於簡單的實施。 因爲我有可變長度模式,所以我使用修改過的Rabin-Karp來散列每個模式的前n個字符,其中n是最小模式的長度,那麼N就是我的滾動子字符串搜索窗口的長度。 對於阿霍Corsick我用this

在我的測試中我兩個文件報紙文章搜索1000種模式,跨越1000次迭代等等均 標準化的完成時間爲:

AhoCorsick: 1

RabinKarp:1。8

樸素搜索(檢查每個圖案&使用string.contains):

http://www.seas.gwu.edu/~simhaweb/cs151/lectures/module5/module5.html

http://www.cs.princeton.edu/courses/archive/spr09/cos226/lectures/18SubstringSearch-2x2.pdf:50個


*一些描述在下面的答案中提到的交易算法資源

http://www-igm.univ-mlv.fr/~lecroq/string/index.html *

+0

順便說一句 - 這不是作業 - 但是一個現實世界的問題! – Joel 2009-11-19 18:42:09

+0

與候選字符串相關的輸入字符串有多長? – 2009-11-19 18:43:06

+0

他們很短。輸入字符串通常少於40個字符,候選字符串也是如此。 – Joel 2009-11-19 18:47:08

回答

25
+0

可以在http://stringsearchalgorithms.amygdalum.net/ – CoronA 2017-03-31 05:19:38

11

將候選字符串集合轉換爲確定性有限狀態自動機,然後在線性時間內運行輸入字符串。將單個字符串轉換爲DFS在標準書籍中已有很好的介紹。您可以通過首先構造一個非確定性自動機然後確定它來轉換一組字符串。在最壞的情況下,這會導致機器人規模的指數式爆炸,但之後的搜索速度很快;特別是如果目標字符串很長,並且候選人短時間工作良好。

+0

提及FSM的+1。絕對是最快的解決方案。 – Anton 2009-11-19 18:42:08

+0

考慮到輸入字符串和候選字符串都非常短,例如<50個字符? – Joel 2009-11-19 18:49:15

+0

@Joel我認爲這取決於你寫上面的意思,「我想在許多輸入字符串中重複執行此操作」。 DFS不依賴於輸入字符串,所以如果候選字符串集合在多個輸入字符串中是恆定的,那麼這相當於一個長輸入字符串,因此該解決方案肯定是相關的。如果所有字符串都很短並且候選人每次都會改變,那麼它可能不是最佳解決方案。 – 2009-11-19 18:53:41

2

你可能要考慮Aho-Corasick algorithm和相關算法。我不知道任何實施這個的圖書館,但這是解決這個問題的經典方法。

+0

Thx上找到幾種算法(包括Aho-Corasick)的集合。 Java實現在這裏:http://hkn.eecs.berkeley.edu/~dyoo/java/index.html – Joel 2009-11-20 11:33:47

6

這是正則表達式的用途。如上所述,有限狀態自動機是您需要的,但這正是如何實現標準的正則表達式匹配器。

在java中你可以寫這樣的:

StringBuilder sb = new StringBuilder(); 
bool first = true; 
for (String subStr : substrings) { 
    if (first) 
     first = false; 
    else 
     sb.append('|'); 
    sb.append(escape(subStr)); 
} 
Pattern p = Pattern.compile(sb.toString()); 

方法escape應該逃避它有一個正則表達式特殊含義的任何字符。

+0

我不能說爲什麼它被拒絕投票,但我可以說,由於Java正則表達式的實現方式,這個正則表達式比單獨搜索每個子字符串的效率要低。 – PSpeed 2009-11-19 19:32:01

+0

我喜歡這個解決方案,並向上投票。但是,我想指出兩個潛在的問題:(1)給定一千個左右的搜索字符串,模式編譯器可能會爆炸。我擔心內存使用會隨着匹配表達的複雜性呈指數級增長。 (2)我相信由模式編譯器構建的FSM/DFS有時會備份。如果是這樣,那麼嚴格推進的專門算法之一可能會更快。 – 2009-11-19 19:41:46

+0

我不認爲我的解決方案是完美的。儘管如此,這可能是足夠的。因人而異。 – 2009-11-19 21:19:41

0

另一種解決方案是使用suffix array作爲INSTR
由於INSTR很小,您可以使用冒泡排序對它進行排序。

之後您可以在O特定CAND串搜索(logN)的時間,
其中N =長度(suffix_array)=長度(INSTR)。

2

我們可以利用字符串的小尺寸(< 50個字符)爲這種情況構建一個超快速算法,代價是內存。

我們可以散列一次所有可能的INSTR子字符串,一次耗費O(n^2)次。然後,無論CAND字符串的數量如何,查找都將是O(1)。值得用於非常大量的CAND字符串。

如果INSTR很大,那麼我們可以構建一個後綴數組並且不對其進行排序,這樣頂部項目是最長的(= N),最下面的項目是INSTR的最後一個字符。現在對於每個CAND字符串,只要從頂部搜索長度(CAND)< =長度(後綴)。每個比較將是O(n)。

+0

我對此有點朦朧,所以我可以在這裏打底,但散列時間是O(n + 1)(n/2)而不是O(n^2),因爲那裏有多少個不同的子串應該? – 2010-01-21 00:01:03

+0

Big-O忽略係數。將'1'和'2'從您的表達式中刪除,並且您留下與'O(n^2)'相同的'O((n)(n))'。 – 2015-07-15 15:14:47

0

Here是Java中快速字符串搜索算法的一些實現。

+0

哪裏?你忘了複製粘貼鏈接嗎? – 2015-12-22 13:13:48

+0

如果您點擊「Here」文本,您將被重定向到使用算法的網站。 – Mike 2015-12-23 16:30:58

0
import java.util.Scanner; 

public class StringMatch 
{ 
    static int temp,i=0,j=0; static boolean flag=true,matcher=false; 

    static String str=null,mstr=null;static char astr[],amstr[]; 

    static void getter(){ 
     Scanner sc = new Scanner(System.in); 
     str = sc.nextLine(); 
     //String str="today is Monday"; 
     astr=str.toCharArray(); 
     mstr = sc.nextLine(); 
     //String mstr="is"; 
     amstr=mstr.toCharArray(); 
    } 

    static void stringMatch(){ 
     while(i<astr.length){ 
      if(astr[i]==amstr[j]){ 
      while((j!=amstr.length)&&flag){temp=i; 
       if(astr[i]!=amstr[j]) {flag=false;matcher=false;} 
       else{matcher=true;} 
       i++;j++; 
       //System.out.println(i+"\t"+j); 
      }if(matcher==true)break;i=temp;}i++;j=0;flag=true; 

     } 
     if(matcher==true) {System.out.println("true");} 
     else {System.out.println("false");} 
    } 

    public static void main(String[] args) { 

    StringMatch.getter(); 
    StringMatch.stringMatch(); 

    } 
}