KMP算法:字符串匹配

KMP算法基本思想:

 我们在用常规的思想做 字符串匹配时候是 如 对如 字符如果 T = abab 用P = ba 去匹配,常规思路是 看 T 第一个元素 a 是否 和P 的一个 b 匹配 ,匹配的话 查看各自的第二个元素,不匹配 则将 T 串的 第二个元素开始 和 P 的第一个匹配,如此 一步一步 的后移 来进行逐个匹配 这样效率不是很高,对于 KMP 算法,其根本就是对 匹配模式串 P 进行 预处理当在匹配的时候可以 选择 联系向 T 后移动几步进行匹配,而不需只是逐步移动》

 如 对于 用 P[1:m] 去匹配 T[1:n ] 当 p[1:i ] 与 T[1+s : j] ( j = i+s) 匹配 ,但是 T[j+1]  != P[i+1] 如此 ,我们必须将 i+s 的 T 起始匹配位置 向后移动,(常规是移动一步进行匹配),如果我们知道 对于 P 的已经匹配子串 p[1:i] 有 前 q 个元素 可以是 p[1:i]的前缀,也是 串 p[1:i] 的后缀那么 我们可以 从 P 的 i+1 的位置开始,和 j+1 的位置开始匹配,也就是所 可以向后移动 i-q 步如后进行匹配。

详情看算法导论:P568

代码如下:

#include <iostream>
#include<string>
#include<malloc.h>

using namespace std;
int fail[100]; 
int *pre;
//  预处理 函数 
void prefix_function(const char const *p,int m){

	int k = 0; // k 表示 匹配 P 的 前 k 个元素 可以 作为 匹配 P 的后缀 也作为需要匹配的位置
	pre[0] = 0;
	pre[1] = 0 ; // 第一个元素 有 0  个元素 与其 后缀匹配
	for ( int q = 2; q<= m; q ++){
		while (k >0 && p[k] != p[q-1]){
			k = pre[k];
		}
		if( p[k] == p[q-1]){
			k++; // k 匹配的个数 加一个
		}
		pre[q] = k;
	}
	for( int i =1 ;i <=m ;i++){
		cout<<pre[i]<<" ";
	}
	cout<<endl;
}
int kmp_match(const char *t,const char *p,int n, int m){
	int q = 0 ; // q 表示 在 匹配 p 在 t 中 匹配的个数
	prefix_function(p,m);
	for( int i = 0 ; i<n; i++){
		while ( q > 0 && t[i] != p[q] ){ // 如果已经 匹配的个数 大于 0 并且 
			q = pre[q];
		}
		if( t[i] == p[q]){
			q = q+1;
		}
		if( q == m){

			cout<< i - m<<endl;
			q = pre[q];
		//	return i-m;
		}
	}
	return -1;
}
int kmp(const char* str, const char* pat){ 
	int i, j, k; 
	memset(fail, -1, sizeof(fail)); 
	for (i = 1; pat[i]; ++i) { 
		for (k=fail[i-1]; k>=0 && pat[i]!=pat[k+1]; 
		k=fail[k]); 
		if (pat[k + 1] == pat[i]) fail[i] = k + 1; 
	} 
	i = j = 0; 
	while( str[i] && pat[j] ){ // By Fandywang 
		if( pat[j] == str[i] ) ++i, ++j; 
		else if(j == 0)++i;//第一个字符匹配失败,从str下个字符开始
		else j = fail[j-1]+1;    } 
	if( pat[j] ) return -1; 
	else return i-j; 
} 

int main (void) {
	char const *t;
	char const *p;
	int n,m;
	string t1;
	string p1;
	cin>>t1>>p1;
	n = t1.length();
	m = p1.length();
	t = t1.c_str();
	p = p1.c_str();
	pre = new int[m+1];
	cout<<n<<"  "<<m<<endl;
	cout<<kmp_match(t,p,n,m)<<endl;
	//cout<<kmp(t,p);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值