KMP算法

KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。

首先,对于这个问题有一个很单纯的想法:从左到右一个个匹配,如果这个过程中有某个字符不匹配,就跳回去,将模式串向右移动一位。这有什么难的?

我们可以这样初始化

之后我们只需要比较i指针指向的字符和j指针指向的字符是否一致。如果一致就都向后移动,如果不一致,如下图:

#include <stdio.h>
#include <string.h>

int BF(const char *src, const char *des, int len1, int len2)
{
	if(src == NULL || des == NULL) return -1;

	if(len1 < len2) return -1;

        int i = 0;
	int j = 0;
	
	while(i < len1 && j < len2) {
		if(src[i] == des[j]) {
			i++;
			j++;
		} else {
			i = i - j + 1;
			j = 0;
		}
	}

	if(j == len2) {
		return i - j + 1;	
	}

	return -1;
}

int main()
{
	char src[] = "abfdushffdfdkl";
	char des[] = "fdush";

	int len1 = strlen(src);
	int len2 = strlen(des);

	int k = BF(src, des, len1, len2);
	printf("%d\n", k);
	return 0;
}

如果是人为来寻找的话,肯定不会再把i移动回第1位,因为主串匹配失败的位置前面除了第一个A之外再也没有A,我们为什么能知道主串前面只有一个A?因为我们已经知道前面三个字符都是匹配的!(这很重要)。移动过去肯定也是不匹配的!有一个想法,i可以不动,我们只需要移动j即可,如下图:

上面的这种情况还是比较理想的情况,我们最多也就多比较了再次。但假如是在主串“iiiiiiiiiiiiiiiiiiiia”中查找“iiiiib”,比较到最后一个才知道不匹配,然后i回溯,这个的效率是显然是最低的。

KMP算法其思想就如同我们上边所看到的一样:“利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。”

所以,整个KMP的重点就在于当某一个字符与主串不匹配时,我们应该知道j指针要移动到哪

 

接下来我们自己来发现j的移动规律:

如图:C和D不匹配了,我们要把j移动到哪?显然是第1位。为什么?因为前面有一个A相同啊:

 如下图也是一样的情况:

 

可以把j指针移动到第2位,因为前面有两个字母是一样的:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void GetNext(const char *des, int *next, int len)
{
	if(des == NULL || next == NULL) return;

    int i = 0;
	int k = -1;

	next[0] = -1;
	
	while(i < len - 1) {
		if(k == -1 || des[k] == des[i]) {
			if(des[++k] == des[++i]) {//小优化
				next[i] = next[k];
			}	
		} else {
			next[k] = k;
		}
	}

}

int KMP(const char *src, const char *des)
{
	if(src == NULL || des == NULL) return NULL;

	int len1 = strlen(src);
	int len2 = strlen(des);

	if(len2 > len1) return NULL;

	int *next = (int *)malloc(sizeof(int) * len2);

	GetNext(des, next, len2);

	int i = 0;
	int j = 0;

	while(i < len1 && j < len2) {
		if(src[i] == des[j] || j == -1) {
			i++;
			j++;
		} else {
			j = next[j];
		}
	}

	free(next);
	if(j == len2) {
		return i - j;
	}

	return -1;
}


int main()
{
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值