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

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

  上星期拜读了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];
            }

        }

    }
}
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值