串的模式匹配

串的模式匹配

串的模式匹配即子串的定位操作,一般把要找的子串称为模式串。

最普通的一种算法思想大概如下:

从主串的第pos个字符起和模式串的第一个字符比较;

如果{

  相同则指向主串与指向模式串的指针都向后移一位,继续比较;

  不同则从主串的下一个字符起和模式串的第一个字符比较;

}

直到指向模式串的指针超出子串的长度,则匹配成功,否则匹配失败。

 

为了避免语言上的歧义,也给大家一个直观的认识,下面找来几幅图描述了这种算法的整个匹配过程。

第一轮(图1-1):

 

串的模式匹配 

第二轮(图1-2):

串的模式匹配

 

第三轮(图1-3):

串的模式匹配

 

第四轮(图1-4)(匹配成功):

串的模式匹配

 

 

这种算法思想直接易懂,在很多情况下也有不错的效率,然而,如果是像主串为“0000000000000000000000001”而模式串为“0000001”时就很浪费时间了,需要很多次的回溯,其时间复杂度最坏能去到O(n*m)

 

KMP算法

针对这种情况有一种很著名的算法:KMP算法。其优点在于当匹配出现不同时,主串不是回溯到最开始,然后向后移一位,如图1-1,图1-2描述的那样。而是直接根据部分匹配的结果,尽量使主串指针往右移,其效果就像下面两幅图展示的那样。

第一轮:

串的模式匹配

 

第二轮:

 

 串的模式匹配

假设主串指针为i,模式串指针为j时发生不匹配时,模式串指针需要变为k。如上面那个例子,则第一轮时i5j5k2

那么一般情况下这个k根据什么确定呢?

观察上面的例子可以发现,”T[0]T[1]…T[k-1]”=”S[i-k]S[i-k+1]…S[i-1]”,这是由k本身的意义决定的,因为主串指针为i,模式串指针为j时发生不匹配时,模式串指针需要变为k,即是说模式串0k-1的字符串必然与主串i-ki-1的字符串相匹配,且这个k值尽量大。

 

另外,由于主串指针为i,模式串指针为j时发生不匹配,因此我们也可以知道”T[j-k]T[j-k+1]…T[j-1]”=”S[i-k]S[i-k+1]…S[i-1]”,因为模式串在j之前的部分必然已经与主串相匹配了。

 

因此我们得到两个k要满足的等式:

”T[0]T[1]…T[k-1]”=”S[i-k]S[i-k+1]…S[i-1]”

”T[j-k]T[j-k+1]…T[j-1]”=”S[i-k]S[i-k+1]…S[i-1]”

还有一些隐含的条件:i>=j>k>0,简化后即j>k>0k要尽量大。

根据两个等式可以发现其实我们只需要关心模式串内部就可以了,即一个等式:

”T[0]T[1]…T[k-1]”=”T[j-k]T[j-k+1]…T[j-1]”( j>k>0k要尽量大)

 

其实从直观上也不难理解,因为主串中对于得到k值有用的信息只是当前主串与模式串已经匹配上的那一段字符串,而这段字符串我们从模式串中同样能得到,因此求得k值可以简化成模式串的内部问题。

 

另外,如果j=0时,k=-1

找不到满足条件的k值时,k=0;

 

next[j]=k,综合起来就是:

 

       -1(j=0)

next[j]=  max{k|j>k>0”T[0]T[1]…T[k-1]”=”T[j-k]T[j-k+1]…T[j-1]”}

       0(其他情况)

 

KMP算法思想如下:

从主串的第pos个字符起和模式串的第一个字符比较;

如果{

    相同则指向主串与指向模式串的指针都向后移一位,继续比较;

    不同则检查next[j] {

             如果next[j]=-1则指向主串的指针向后移一位,指向模式串的指针归零,继续比较;

                  否则主串该字符S[i]和模式串的T[next[j]]字符比较;

         }

}

直到指向模式串的指针超出子串的长度,则匹配成功,否则匹配失败。

 

而如何求得next[j]呢?

 

首先next[0]=-1。设next[j]=k,即满足max{k|j>k>0”T[0]T[1]…T[k-1]”=”T[j-k]T[j-k+1]…T[j-1]”}

next[j+1]是什么呢?

 

如果T[k]=T[j],则易得max{k+1|j+1>k+1>0”T[0]T[1]…T[k]”=”T[j-k]T[j-k+1]…T[j]”},即next[j+1]=k+1=next[j]+1

但如果  T[k]!=T[j]怎么办呢?

其实我们可以把求next[j]的过程也看成一个模式匹配的过程,只不过主串和模式串都是T,而且目的是填上next表。因此T[k]!=T[j]时,我们也可以用KMP算法,使T[j]直接与T[next[k]]进行匹配。

T[j]= T[next[k]]

则易得max{ next[k]+1|j> next[k]+1>0”T[0]T[1]…T[next[k]]”=”T[j- next[k]]T[j- next[k]+1]…T[j]”},即next[j+1]=next[k]+1

T[j]!= T[next[k]],则同理类推,直到某个k值使T[j]= T[next[k]],若没有满足条件的k值,则next[j+1]=0

 

综上,求得next表的算法基本如下:

 1 public void getNext(String T,int[] next){
 2 
 3       int i=0, j=-1;
 4 
 5       next[0]=-1;
 6 
 7       while(i<T.length()-1){
 8 
 9          if(j==-1||T.charAt(i)==T.charAt(j)){i++;j++;next[i]=j;}
10 
11          else{j=next[j];}
12 
13       }
14 
15 }

 

 

转载于:https://www.cnblogs.com/ZoneOfTheBoy/archive/2012/10/10/2718385.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值