您的位置:首页 > 其它

字符串匹配算法研究(一)

2008-02-29 10:10 405 查看

字符串匹配算法研究(一)

  上星期拜读了2004年中国IOI集训队中朱泽园(当时就读南京市外国语学校)的一篇论文《多串匹配算法及启示》,并将其中的部分算法在理解的基础上用C++和Java做了实现。现将本人的理解与代码帖出,请读者不吝赐教。

  首先我把该算法要解决的问题重新描述一下:即给定m个长度不等的模式串S1,S2,...,Sm以及一篇长度为n的正文T,求min({s|整数s∈[0,n-1],且存在整数a∈[1,m],使得T.subString(s,Sa.length-1)=Sa})。例如,当正文T=abcdefgh,S1=cdefg,S2=efg,则s=2。(因为S1出现在T下标为2的子串处,S2出现在T下标为4的子串处,min(2,4)=2)。

  对于解决该问题的实际意义,论文作者已表述得十分清楚:“含逻辑关键字的搜索引擎是这个问题的实际应用。医学家们在DNA序列中,搜索可能为变异的几种模式,也是这类问题的典型。因此用有效算法解决该问题
能大大提高各行业的工作效率。”

  对于如何解决该问题,作者首先给出了最容易相到的枚举法及其优化。由于该想法太过简单,只要稍具编程基础的人都能想到因此我就不再讨论了。总而言之,用枚举法解决该问题的时间复杂度为O(n*sum({Si.length|i=1,2,...,n}))。而对于问题的规模而言,明显枚举法是力不从心的。

  为了有效地解决该问题,作者先将问题简化。即当模式串只有一个时,多串匹配问题就变成了单串匹配问题。对于单串匹配作者发现了一现成的算法:kmp(Knuth-Morris-Pratt)算法。kmp算法的功能如下:

  输入:模式串P(长度为m),正文T(长度为n)
  输出:集合{s|整数s∈[1,m+n-1]且T.subString(s,m-1)=P}

  kmp算法的主体思想是先给出模式串的前缀函数(定义:Pi(j)=max{j|P.subString(0,j)=P.subString(i-j,i),j∈[0,i]}),对模式串进行预处理,之后在拿原文和模式串比较时,可根据模式串的前缀函数来移位,减少不必要的比较次数。现给出代码:




/** *//**


*


* @author liuxy


* 用Kmp算法解决单串匹配问题


*/




public class Kmp ...{






/** *//**


* 对模式串p进行预处理,计算其前缀函数,算法如下:


* k<-0


* pi[0]<-0


* For i<-1 to p.length do


* while k>0 and p[k]!=p[i] Do k<-pi[k-1];


* If P[k]=p[i] Then k<-k+1


* pi[i]<-k


* End For


* 算法时间复杂度为O(p.length)


*


* @param p


* @return int[]


*/




public static int[] kmpInit(String p)...{


int[] pi = new int[p.length()];


pi[0] = 0;


int k = 0;




for(int i = 1; i < p.length(); ++i)...{


pi[i] = 0;


while(k > 0 && p.charAt(k) != p.charAt(i))


k = pi[k-1];


if(p.charAt(k) == p.charAt(i))++k;


pi[i] = k;


}


return pi;


}






/**
*//**


* kmp主算法,算法如下:


* run kmpInit(p)


* q<-0


* For i<-0 to text.length Do


* while q>0 and p[q]!=text[i] Do q<-pi[q-1]


* If p[q]=text[i] Do q<-q+1


* if q=m Then


* write "在i-m+1处出现模式串"


* q<-pi[q-1]


* End if


* End For


* 算法时间复杂度为O(text.length+p.length)


*


* @param text


* @param p


*/




public static void kmp(String text, String p)...{


int n = text.length(), m = p.length();


int q = 0;


int[] pi = kmpInit(p);




for(int i = 0; i < n; ++i)...{


while(q > 0 && p.charAt(q) != text.charAt(i))q = pi[q-1];


if(p.charAt(q) == text.charAt(i))++q;




if(q == m)...{


System.out.println(i-m+1);


q = pi[q-1];


}


}


}


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: