2009-11-19 68 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(); 

    } 
}