KMP算法

KMP算法

算法用途

:用于两个字符串的匹配,称为字符串匹配。例如母串"ABCDEF"与子串"BCD",因为子串可以在母串中找到,所以他们是匹配上了的。

算法思想

利用子串的Next数组(下面会详细讲),在匹配时若匹配不上,无需从子串的头部开始,可以直接从Next[i]开始匹配,大大减小了原本暴力的做法。

Next数组

Next[i]表示在子串中(0~i-1)中的前缀和后缀的最长长度。
表一
  我们的Next[0] = -1是因为S1[0]前无元素,为了方便后续代码编写,特殊赋值为-1。

匹配过程

在这里插入图片描述
我们已经用眼睛求得了S1的Next数组,接下来开始匹配(以数组下标为0为基准),使用两个下标指针,用L遍历S1,R遍历S。
①当L = R = 3时发现S[R] != S1[L]
②令L = Next[L] = 1
③继续匹配,此时L = 1,R = 3,但发现S[R] != S1[L]
④令L = Next[L] = 0
⑤继续匹配,此时L = 0,R = 3,但发现S[R] != S1[L]
⑥令L = Next[L] = -1
⑦继续匹配,此时L = -1,R = 3,此时含义为S1的下标已经到达-1,意味S1[0] != S[R],此时直接令L++,R++
⑧继续匹配,此时L = 0,R = 4,发现S[R] == S1[L],直接L++,R++
⑨继续匹配至L = 3,R = 7时,发现S[R] != S[L],此时令L = Next[L] = 1
⑩继续匹配,此时发现S[R] = = S[L],继续R++,L++,直到最后我们匹配上了。

int KMP(char *S,char *S1) {
	int l = 0,r = 0;
	while(r<strlen(S)) {
		if(l == -1||S1[l] == S[r]) {
			l++;
			r++;
		}
		else l = Next[l];
		if(l==strlen(S1)) return r-l+1;
	}
	return -1;
}

接下来就是重头戏了,我们还需要求Next数组,才可以完成上述操作。

我们这里以子串为“a b c a b d a b c a b e a”为例
Next[i]表示回溯的位置,例如表一中的Next[3] = 1,因为aba中最长的相同前后缀为’a’与‘a’。我们在求他的时候,利用两个指针q与p,q初值附为-1,p附为0。我们发现,S1[0]前没有元素,那么就先让Next[0] = -1。在这里插入图片描述
①当q == -1时,代表i~p-1内最长相同前后缀长度为0,故令Next[p] = q+1 = 0.
②当S1[q] = = S1[p]时,因为最长前后缀在增长,所以我们直接让Next[p] = Next[p-1] + 1.
在这里插入图片描述
③当S1[q] != S1[p]时,我们这时无需令q等于0去进行从头开始,比如上图,我们发现运行到此时,S1[0,q-1]一定是与S1[p-q,p-1]一定是相等的,因为不相等就运行不到这一步,那么我们又发现S1[0,Next[q]-1]与S1[q-Next[q],q-1]是一定相等的。那么S1[0,Next[q]-1]与S1[p-Next[q]-1,p-1]一定相等。所以我们此时令q = Next[q],这样就可以大大加快速度。

void ToNext(int *Next,char *S1) {
	int q = -1,p = 0;
	while(p<strlen(S1)) {
		if(q == -1|| S1[q] == S1[p]) {
			q++;
			p++;
			Next[p] = q;
		}
		else q = Next[q];
	}
	return ;
}

效率分析

  我们在上述匹配过程中,发现当S[R] != S1[L]时,我们并非直接让L = 0从头开始匹配,而是让L = Next[L] ,之后再匹配S[R]与S1[L]。这么做是因为我们的Next[L]的含义——子串S1中0 ~ L-1中的前缀与后缀的最长长度,其实也等于了下标。那么我们直接让L = Next[L],此时子串中0~Next[L]-1中的元素一定与当前L-Next[L] ~ L-1中的元素对应相等,这样效率就极大的提高了。

时间复杂度分析

  时间复杂度 = O(m+n),匹配时,最坏R从0移到m,过程中虽然L向前推了,但是S1相对于S还是向后移动,最坏匹配到最后一个元素,所以L也相对于S向后移动了m个长度。所以匹配时的复杂度是2 * m。
求Next数组时,我们也可以类比成S1与其自身的匹配,故复杂度为2 * n。所以总的时间复杂度为O(2n+2m)简写为O(n+m)。

写的很烂,有错请指出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值