字符串匹配算法原理及其实现——KMP算法篇

1.字符串匹配概念

所谓的字符串匹配其实是一个很常用的概念,小到我们平常游戏中见到的敏感词屏蔽,大到各种场景下的查找操作都会用到字符串匹配。字符串匹配就是在文本串中搜索模式串有没有出现同样的字符串以及它的出现位置。

2.KMP

2.1原理

字符串的匹配中比较常用的是暴力破解法,也称为BF。
在第一个文本串和模式串对齐方式中,我们先找到一个匹配的位置,然后,从这个位置之后,文本串和模式串同时向后移动,直到一个不匹配的位置。那么此时问题来了,依照BF方法,我们应该做的就是重新“后退” 找到一个新的匹配位置,这种方法显然在面对大量字符串时搜索的时间会指数性的增长,并且在这之前匹配出的结果没有得到有效的利用导致浪费,有什么办法可以利用之前匹配出的结果来改善BF方法吗?

答案是肯定的,KMP算法就是这种方法的改善型算法。具体改善的地方是:当出现失配时,不必像BF一样回溯,而是由上一次匹配到的位置向后,继续移动至下一个匹配的位置,
由此我们可以看出:
1.移动对齐方式只由文本串与模式串失配位置决定。
2.而与文本串与模式串失配位置的文本串字符无关。
3.也就是说,移动对齐方式只与模式串有关。
那么,我们该如何判断相应的距离并且可以做到快速访问呢?答案就是用一个数组来实现,这个数组我们叫做next数组。

下面是next数组的创建过程,它的核心部分是若匹配到模式串和文本串相等的位置,则两个串的位置同时加1,并且把这个位置记入next数组;反之,则从next数组里找到最近的匹配到的位置并从这里开始继续匹配。(核心代码只有三句话,大家也可以想一下有没有方法还可以继续改进这个算法)

void CreatNext(string p, int next[]){
	int len = p.size();
	int i = 0, j = -1;
	next[0] = -1;
	while (i < len - 1){
		if (j < 0 || p[i] == p[j]){
			i++;
			j++;
			next[i] = j;  
		}
		else{
			j = next[j];
		}//kmp算法的三句核心部分。。。
	}
}

由此,我们可以知道,KMP算法的特点是:

模式串和文本串的匹配是由左向右依次进行的,一旦出现不匹配的位置,则模式串从next数组中得到需要移动到的位置,并重新进行匹配。

2.2改进

我曾经在别的网站看到过一个问题,

4由上图可以看出,I和II是匹配的,III和IV是匹配的,按照上面的匹配方式II和IV的下一个字符对应是next数组中的记录3,但是实际上如果IV的下一个字符发生了不匹配,因为III和IV的下一个字符都是A,因此即使将III移动到IV的位置上也还是会发生失配,这样移动就没有任何意义了。所以我们才可能需要改进一下next数组。代码如下:

void CreatNext(string p, int next[]){
	int len = p.size();
	int i = 0, j = -1;
	next[0] = -1;
	while (i < len - 1){
		if (j < 0 || p[i] == p[j]){
			i++;
			j++;
			next[i] = p[i] == p[j] ? next[j] : j;  //其他位置没有改变,仅仅改变了一下这里
		}
		else{
			j = next[j];
		}
	}
}

 

你可能感兴趣的:(c++,字符串)