KMP算法

一开始感觉KMP算法非常难,但是其实多搜一些资料仔细研究一下发现其实。。

其实还是学习方法的问题吧,学的太潦草,不深入,仔细钻研,发现其实并不是那么难。

KMP算法是字符串匹配算法。

给定两个字符串a,b。统计串b在a中出现的位置与次数。

a= bacbababadababacambabacaddababacasdsd

b= ababaca

1.暴力枚举

bacbababadababacambabacaddababacasdsd

ababaca

每次让b串向后移一位。

bacbababadababacambabacaddababacasdsd

  ababaca

2.借助next数组来实现。

    KMP算法的核心也就在这个next数组上面。它主要存储了当匹配失败时,串b需要后移的位数。我们不单单是后移一位,我们预先处理出需要后移的位数,那么因为是预处理,所以next数组的计算也就和串a没有半毛钱关系。

next[j] = k.最关键的一条语句了。

假设现在有两个串,每个串从0开始存储:

S = ABCABCDHIJKOOL

T = ABCABB

在第5位发现匹配失败:

这时我们的移动方案:

S = ABCABCDHIJKOOL

T =        ABCABB

移动完后我们直接从T串的第C开始继续匹配就OK。

显然,我们不可能真正的去移动,我们需要用两个指针(l,r)去模拟。

第一次在第五位发现匹配失败时,l = 5, r= 5.

然后移动后l = 5, r = 2.

显然当匹配失败时,r要移动到下一个位置,而l是不动的。r要移动到第k位

我们观察上面的移动可以发现:

T串的前k个字符和r之前的最后k个字符相同,这里的r是指匹配失败时的r。

这里我们移动到了第2位,显然k=2.

T串的前2位:ABCABB

r之前的最后K位:ABCABB

显然r的取值范围是0~length(串的长度)-1。

我们用一个next数组来保留每一个r需要后移的位数K。

 

4.计算next数组。

next数组的计算只需要用到T串。

我们可以利用一种dp的思想来处理。

依然用这个串T = ABCABBC

我们先考虑j=0时,这个时候r无法移动,只能右移l。所以初始化为-1,即next[0] = -1;

再考虑j = 1,r只能移动到第0位。next[1] = 0;

j  = 2,T[0] != T[1], next[2] = 0;

j = 3, next[3] = 0;
j =  4, next[4] = 1;

j = 5, next[5] = 2;

我们在计算next[5] 时显然可以利用next[4],因为T[j - 1] == T[next[j - 1]], 所以next[j] = next[j - 1] + 1.

那么

    next[0] = -1;
	int j = 0, k = -1;
	while(j < m - 1){
		if(k == -1 || T[j] == T[k]){
			next[++j] = ++k;
		} else k = next[k];
	}

显然当T[j - 1] != T[k]时, next[j] = 0; 

k = next[k] :

看这个串:ABACDABABC, C是第九位,此时k = 3, j =  8.(ABA)

此时C != B,  k = next[3], 这里next[3]>0,j的前面有部分和整串的前缀相同, 所以我们可以直接把T[next[k]]和T[j]比较。

再看ABAB这个串,显然next[3] = 1;但是我们真的有必要从1再比较吗?没必要,因为T[1] = T[3]。

所以,上述代码可以再优化一下:

next[0] = -1;
	int j = 0, k = -1;
	while(j < m - 1){
		if(k == -1 || T[j] == T[k]){
			if(T[++j] == T[++k]) next[j] = next[k];
			 else next[j] = k;
		} else k = next[k];
	}

5.KMP比较

最后一步了,拿两个指针去模拟就行了。

#include<cstdio>
#include<cstring>
int n, m, l, r, k, ans;
char S[120], T[120];
int next[120];
int main(){
	scanf("%s%s", S, T);
	n = strlen(S);
	m = strlen(T);
	next[0] = -1;
	int j = 0, k = -1;
	while(j < m - 1){
		if(k == -1 || T[j] == T[k]){
			if(T[++j] == T[++k]) next[j] = next[k];
			 else next[j] = k;
		} else k = next[k];
	}
	l = 0; r = 0;
	while(l < n && r < m){
		if(r == -1 || S[l] == T[r]){
			l++;
			r++;
		}else r = next[r];
		if(r == m){
			printf("%d ", l - r);
			ans++;
			r = 0;
		}
	}
	printf("\n%d", ans);
	return 0;
}

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值