KMP原理详解-不一样的代码

KMP原理详解-不一样的代码

今天,博主又写了一次kmp算法,其实这个算法是很难理解的,为什么很难理解,是因为,这个算法本质上是需要证明过程的,但是很多人在讲解这算法的时候,只是粗略带过,他们可能也没有真正的去理解这个算法。

对于KMP算法,我首先来讲一下这个算法的起源思想:

这个算法的起源是怎样的
加入我们有一个字串 比如: abcabcef
又有一个总串:abcabdfaabcabcacefa

我们希望去匹配是否总串 abcabdfaabcabcefefasdfa 含有字串 abcabcef,和所在位置

简单的方法肯定就是暴力枚举,但是这种方法时间开销很多时候是很大的。

所以我们希望简化,于是人们发现:
abcabdfaabcabcefefasdfa
abcabcef

匹配到第一个d 之后,就不能继续匹配了,这个时候,有人说,我们从头开始匹配吧,从之后的b开始重新匹配,但是呢,人们发现,如果重新开始匹配实际上可以节省一些步骤,
比如我们发现实际上可以从第二个a开始匹配其他的可以省略了,因为b c显然和第一个a是不相同的。
也就是说我们希望是不是可以找到一个方法可以简化我们的匹配机制:
我下面列举一下,如果到 abcab匹配失败,我们下面会怎么去匹配
如果失败,我们肯定首先考虑bcab和abca是不是匹配的,是不是这样
下面一步是不是这样:
abcabdfaabcabcefefasdfa
abcabcef
发现明显不匹配,我们下面一步是不是这样:
abcabdfaabcabcacefa
abcabcef
发现还是不匹配,我们下面怎么做:
abcabdfaabcabcacefa
abcabcef
就是,每次不匹配实际上我们是不是会往前挪一次总串的匹配字符,直到匹配。
但是我们就要考了了我们是不是可以不用一次一次挪呢?
那我们怎么做?

我们发现,如果匹配带某一步之后出现了不匹配,我们会不断将将总串匹配位置往前挪一个字符,再跟字串匹配,但是挪到哪里呢,是不是一直挪到总串可以和字串前面进行匹配。
我们可以去维护这个样一个数组,这个数组可以告诉我们直接挪到那个位置:
比如,我们像下面这样的字符串,匹配到d不能继匹配了,我们可以直接从第四个位置开始匹配。
abcabdfaabcabcefefasdfa
abcabcef
那么我们其实在做的是什么?

假设从第一个字符a开始匹配,我们不能匹配,下面就将总串往前挪一个位置
如果匹配到第二个字符b,发现不能匹配,我们肯定考虑a是不是能跟当前字符匹配。

如果匹配到第三个字符发现不能匹配,我们怎么做,首先肯定看前第一个字符是不是能跟前一个字符匹配,如果不能再考虑a是不是能跟当前字符匹配。
就是我们如果不能继续匹配了,我们是不是希望找到最大的能跟前面可以匹配的前缀字串。

所以,我们需要求解的就是,如果在一个位置匹配失败了,我们希望找到最大前缀可匹配字串。
所以,对于kmp算法一种新的思路如下:
1.输入字符字串,希望得到可以跳转的索引数组kmpindex(kmpindex为如果匹配失败,我们该如何到那个字符)
2.设置当前可匹配最大前缀字串长度jmax=0
3.遍历字串
3.1对于字串第一个位置,我们知道如果第一个字符都匹配失败了,我们是不是没地方跳转了,所以kmpindex[0]为一个负值,但是一般设置-1,其实设置成其他的也可以,只是需要处理麻烦一点。
3.2对于第二个位置,我们知道,如果第二个字符匹配失败了,我们是不是该看看当前字符是不是第一个字符匹配,那如果,第二个字符也等于第一个字符,是不是可以直接跳过了,就是都不能匹配了,kmpindex[1]=kmpindex[0]。如果不相等那么kmpindex[1]=0。
3.3对于大于等于第二个位置我们知道,如果当前不能匹配,我们希望找到下一个可以尝试匹配的前缀字串。

这里我们把代码给出来:

#include
#include

void kmp(char *subs,int * kmpindex,int n) {
	int i;
	printf("%s ", subs);
	int jmax = 0;
	for (i = 0; i<n; i++) {
		//printf("%c ", subs[i]);
		
		if (jmax == -1) {
			
			kmpindex[i] = 0;
			
			jmax++;
			continue;

	  }
		

		if (i == 0) {
			kmpindex[i] = -1;
			continue;
		}
		else if (i == 1) {
			if (subs[i] == subs[0]) {
				kmpindex[i] = -1;
			

			}
			else {
				kmpindex[i] = 0;

			}
			continue;
		}
		else {
		//	printf("%d  %d \n", jmax, i);
		
			if (subs[jmax] == subs[i-1]) {
				jmax++;
				kmpindex[i] = jmax;



			}
			else {
				//printf("%d  %d \n", jmax, i);

				jmax = kmpindex[jmax];
				i--;
				
				

			}


		}
		
	}


}

int main() {
	int i = 0;
	char s[100] = "adbcdbdadfcsacsdjhfghasbfkjbfshdshafhsbfbsbfsjabdfhsbajfbnsdkjfnsafsdafdsdgsdfasfsafsadfsa";
	char subs[29] = "abcabceddecaacdeedeed\0";
	int kmpindex[29];
	

	kmp( subs, kmpindex, 18);
	for (i = 0; i < 18; i++) {
		printf("%d ", kmpindex[i]);
	}
	return 0;
}


其实kmp算法是类似动态规划算法的。

我们在做kmp算法的时候,掌握住三个核心要点
1.得到的数组是如果匹配失败,下次字串改跳转的下标,即下次和谁匹配,nextval返回的是匹配失败之后下次应该从哪里开始匹配的位置。
2.下次匹配肯定是从最大可匹配的前缀字串开始匹配的,所以我们必须维护一个最大前缀字串长度。
3.nextval返回的是当前匹配失败后,当前位置之前的字串,前缀和后缀的最大匹配长度。

你可能感兴趣的:(c++与c语言,python)