Knuth-Morris-Pratt Algorithm

本文深入讲解KMP算法,探讨其高效精确匹配原理,通过预处理避免重复计算,介绍前缀函数与fail数组构造,提供详细代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  Today , 第一次学习KMP Algorithm,其中好多地方还是不能理解的透彻,本文将进一步对 KMP Algorithm 进行学习,搞清楚其中的思想……

  First , KMP Algorithm is best known for liner time for exact matching ,  (Runing time is O(Length(S)+Lendth(P)))  Because Preprocessing is O(P) , Matching is O(Length(S)) , 效率很 high , 成功的避免了Recomputing Matches ;

    若想 Avoid Recomputing Matches , 就需要 Preprocessing ,对于 Preprocessing ,就是找出字符串P中的 Repeat char can backtrack position by prefix-function;

字符串P为:

ababaca

 

 

 

字符串S为:

bacbabababacaab

 

 

  定义两个指针 i 和 j ,i  是指向 字符串 S 中的第 i 个数据元素 , j 是指向字符串 P 中的第 j 个数据元素 ,用指针 i 和  j 分别表示 S[ i - j + 1 , …… i ] 和 P[ 1 , …… j ] 中的数据元素完全相等, 随着 i 的值不断增加 , j 的值也在不断变化 ,通过对字符串 P 进行 Preprocessing 得到 fail数组 ,j 的值变化有两种可能:1、如果 P[ j + 1 ] 与 S[ i + 1]相等,j 应该加 1 ; 2 、如果 P[ j + 1 ] 与 S[ i + 1 ] 不相等 ,则 j 应该等于 fail数组中第 j 个数据元素的值 ;fail数组就是用来记录 当P[i + 1] 与 S[ j + 1 ] 不相等时 , j 的变化 ;

 

下面介绍一下,Prefix - function ,即如何确定fail 数组 ;

m ← Length[ P ] ;

k = 0 ;

fail[0] ← 0 ;

for q ← 1 to m  do

  while k > 0 and p[k] ≠ p[q]  do

    k = next[k-1] ;

  end while 

  if(p[k] = p[q] )

    k ← k + 1 ;

  end if

  next[q] = k ;

end for

 

 

这样即可得到 fail数组 ;   

 

得到 fail 数组之后,就可进行字符串P 与 字符串S 进行匹配了 ,具体匹配过程,下面给出相应的伪代码:

n ← Length[ S ] 

m ← Length[ P ]

k = 0 ;

for i ← 0 to n  do 

  while k > 0 and p[ k ] ≠  S[ i ]  do

    k = fail[ k -1] ;

  end while

  if p[k] = S[i]  then

    k ← k + 1

  end if

  if k == m then  

    return i - m + 1

  end if

end for 

return  -1 

 

 

 下面给出KMP算法的详细代码过程:

#include<iostream>
#include<string.h>
using namespace std ;

int fail[1000] ;

void prefix( char *p )	{
	int len = strlen(p) ;
	int k = 0 ; 
	fail[0] = 0 ;
	for( int q = 1 ; q < len ; q++)	{
		while( k > 0 && p[k] != p[q] )   // K > 0 的原因是为了让后面的 s[q] 先和 s[0] 比较保证找到后面的能够出现和第一个相等 ; 
			k = fail[k-1] ;
		if(p[k] == p[q])
			k++ ;
		fail[q] = k ;
	}
}

int kmp( char *s , char *p )	{
	int len1 = strlen(s) , len2 = strlen(p) ;
	int k = 0 ;
	for(int q = 0 ; q < len1 ; q++ )	{
		while( k > 0 && s[q] != p[k] )  // K > 0 为了保证后面的和第一个比较出现相等的 ;
			k = fail[k-1] ;
		if(s[q] == p[k])
			k++ ;
		if(k == len2)
			return q - len2 + 1 ;
	}
	return -1 ;
}

int main()	{
	char s[1000] , p[1000] ;
	cin >> s >> p ;
	prefix(p) ;
	if(kmp(s,p) != -1)
		cout << kmp(s,p) << endl ;
	else
		cout << "NO" << endl ;
	return 0 ;
}

  

 

 

 

      

转载于:https://www.cnblogs.com/NYNU-ACM/p/4237469.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值