KMP算法

KMP算法的全称为:Knuth-Morris-Pratt 。朴素算法效率不高根本原因在于进行了多次重复的比较,没有充分利用已匹配的字符的信息。设文本为T,匹配模式为P;例如:

          T : a b a b c d a b b a b a b a d

          P  :   a b a b a b a

上面模式P与文本匹配,在从开始匹配到字符T[4]!=P[4]时,普通做法是将字符串后移一位,继续从开头匹配。在KMP算法中,我们有已知信息:P[0...3]==T[0...3],也就是说这一部分是匹配好的,如果下次匹配仅仅移动一位,那么刚才匹配好的子串之间对应关系如下:

         T[0..3] : a b a b

         P[0...3]:   a b a b

显然划线部分就是不匹配的,更何况整个串匹配,所以这是一个无用的移位。如果这次移动的是2位,则如下:

          T[0...3] : a b a b

          P[0...3]:        a b a b

显然这次子串部分匹配上了,这时候再去检查剩下部分是否匹配就可以了。至于如何得知每次该移动的位置,我们需要填一个Next[]表。

    首先弄清楚字符串的前缀和后缀。设x,y,z是三个非空串xy表示两个串的连接,若x=yz ;则y是x的前缀,z是x的后缀。比如abab的前缀有:a,ab,aba。后缀有:b,ab,bab .我们定义的Next[j]就是表示在P[0...j-1]中前缀与后缀匹配的最大长度。j可以认为是P[0...1-j]的长度。对于P的Next[j]表格如下:

     j           0   1   2    3   4   5  6 

 Next[j]    -1    0  0    1   2   3  4

有了Next[j]表格,下次在P[j]处不匹配时,只需要移动j-Next[j]的距离。Next[0]=-1表示模式P应该移动到不匹配文字后面去。计算Next[j]就是字符串P与本身的匹配.设Next[0]=-1假设Next[i-1]已经确定,现在要来确定Next[i] 。分两种情况:

                      1)设 j= Next[i-1] ,若P[i-1]==P[j] ,这说明当前的后缀比前次的后缀长度多一个字符,于是Next[i]=j+1.

                      2)若 P[i-1]!=P[j], 这时应该去 P[ 0...j-1中去找匹配的串,j=Next[j] ,比较P[i-1]与P[j ].如此循环,当遇到相等或者遇到j==-1为止,然后Next[i]=j+1;

在KMP的算法中,在T的每个字符至多比较两次。最后的时间复杂度为O(m+n),预处理时间为O(m).主要是弄清楚Next[j的含义。

<span style="font-size:14px;">#include<iostream>
#include<string>
using namespace std;
#define SIZE 10003
int Next[SIZE];
int ans;
int postion[1000];
int main(){
	int i, j, m, n;
	Next[0] = -1;  
	string P, T;
	cin >> P >> T;
	m = P.size();
	n = T.size();
	for (i = 1; i <=m; i++){    //计算Next[j]
		j = Next[i - 1];
		while (j != -1 && P[i - 1] != P[j])
			j = Next[j];               
		Next[i] = j + 1;
	}
	//--------------------------------------------------------
	ans = i=j=0;
	while (i <n){
		while (j == -1 || (j < m&&P[j] == T[i])){  //j=-1表示第一个字符都没有匹配上
			j++;
			i++;
		}
		if (j == m)
			postion[ans++] = i - m;
		j = Next[j];        //更新j的位置
	}
	//-------------------------------------------------------------------
	for (i = 0; i < ans; i++)
		cout<<postion[i] << endl;
	return 0;
}</span>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值