串的模式匹配 : KMP算法


当存在 [1] 这样的现象,[2] 这样的做法,是否合理以及正确,或者有 [1] 这样的现象,为什么就可以有 [2] 这样的做法 ?

算法思想

[1] 当主串的第i个字符与子串的第j个字符失配时,若子串的前(k-1)个字符和子串的后(k-1)个字符匹配,‘p1……pk-1’=‘pj-k+1……pj-1,则只需主串 S 的第 i 个字符与子串 P 的第 k 个字符开始向后比较即可,i 不必回溯。
[2] k的值只与子串的组成有关,而与主串无关,即 k 值与 j 失配位置之前的子串的结构相关,每一个 j 位置对应一个k值,用next[j]存储,表示当模式串中第 j 个字符与主串中第 i 个字符失配时( Si ≠ Pj) ,下一次模式串 P 中第next[j]个字符与主串 S 的第i个字符比较

简而言之

若子串中,‘p1……pk-1’=‘pj-k+1……pj-1’,则在 j 处的失配,i 不回溯,直接和子串的 k 位置比较即可,k位置用 next[j] 存储。
在这里插入图片描述

问题提出

其实,算法思想中[1]是现象,也就是子串中需要存在这样的前后缀相等时,[2]是做法,表示在失配的位置 j ,模式串应该向右滑动 k 距离。那么当存在这样的现象,这样的做法,是否合理以及正确?或者有 [1] 这样的现象,为什么就可以有 [2] 这样的做法 ?很多地方的讲解都是利用这样的做法去解释现象,或者因为有这样的现象,就可以有这样的做法,没有揭露根本的本质原因,发现自然规律,然后利用自然规律去做一件巧妙的事情得到了很好的结果,但还是要解释清楚这样的这样的规律是如何带来这样巧妙的结果的。

数学证明

[1] 朴素的串匹配算法
[2] KMP算法证明
证明思想:在 j 处失配,下一次直接让 next[j]=Pk 和 i 匹配,则需要证明 Si-j+2,…, Si-k这 j-k-1 个数不用再和P0比较(即一定会匹配失败),直接让P1和Si-k+1进行比较,即Pk与Si进行比较
根据已知条件得到:
在这里插入图片描述
递推推导
在这里插入图片描述

求解next[j] 数组

在这里插入图片描述
求解的思想就是数学证明的思想,利用递推来求解:
(相等,不相等(相等,不相等(相等,不相等(…))))
已知next[1]=0,next[j]=k,如何由next[j]求得next[j+1]?(得到一个递推式)
1oP[j] == P[next[j]],则next[j+1] = k+1(即next[j+1] = next[j]+1)
在这里插入图片描述
2oP[j]≠P[next[j]],则令P[j]P[next[next[j]]]比较:
若相等,则
next[j+1] = next[next[j]]+1

若不等,则沿失败链继续查找,直到某个P[next[…next[j]…]] == P[j],或next[…next[j]…]==0,这时都置next[j+1] = next[…next[j]…]+1

代码实现:
已知:next[1]=0;设设next[j]=k,则1<k<j,满足:‘p1……pk-1’=‘pj-k+1……pj-1
1、若pk == pj,则next[j+1]= next[j]+1;
2、若pk≠ pj,此时看作模式串作为子串与自身在第k位失配,应从next[k]位开始与j位比较,若pnext[k] ≠ pj,即重复 2 直至相等或初始条件next[1]=0;若pnext[k] = pj ,则next[j+1]=next[k]+1。

int Get_Index(sstring T, int next[]) {
//求模式串T的next函数值并存入数组next
  i=1;  next[1]=0;  j=0;
  while(i<=Strlength(T)) {
        if ( j==0 || T[i]==T[j]) {++i; ++j;next[i]=j;}
        else  j=next[j]; 
  }
} // Get_Index

注:可能存在next[j+1]与next[j]相等,故在pk= pj时改为next[j+1]=next[j],则代码改进如下:

int Get_Index(sstring T, int next[]) {
//求模式串T的next函数值并存入数据next
  i=1;  next[1]=0;  j=0;
  while(i<=Strlength(S) && j<=Strlength(T)) {
        if ( j==0 || T[i]==T[j]) {
		++i; ++j;
		if (T[i]!=T[j])  next[i]=j
		else  next[i]=next[j];
        else  j=next[j]; 
	    }
  }
} // Get_Index

KMP代码实现

int Index(sstring S,sstring T,int pos) {
//返回子串T在主串S中从第pos个字符开始的位置
//要求T非空,1≤pos ≤Strlength(S)
  i=pos;  j=1;
  while(i<=Strlength(S) && j<=Strlength(T)) {
        if (S[i]==T[j]) {++i; ++j;}
        else {j=next[j];} 
  }
  if (j> Strlength(T))  return  (i-Strlength(T));
  	    return 0;
} // Index
发布了36 篇原创文章 · 获赞 78 · 访问量 4822

猜你喜欢

转载自blog.csdn.net/qq_40263477/article/details/104561573