总结字符串匹配算法(KMP算法)

什么是KMP算法呢?

KMP算法是一种改进的字符串匹配算法,其核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。
            KMP算法的时间复杂度O(m+n)。

如下图所示,假设有两个字符串,假设将字符串Ar称之为主串,将字符串称为模式串,现在使用一个算法去判断模式串是否与主串相匹配,或者是主串的一部分。

当使用普通的算法去判断两个字符串是否匹配时,我们会把Ar[0] 跟 Br[0] 匹配,如果相同则匹配下一个字符,出现不相同的情况,我们会丢弃前面的匹配信息,然后把Ar[1] 跟 Br[0] 匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了算法的匹配效率。

 在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配,右移的距离是这样计算的:在匹配的模式串子串中找出最长长度的相同前缀和后缀,然后进行移动,使他们重叠。

在这里说一下什么是前缀和后缀。举个例子会清晰的表示前缀和后缀。假如有一个字符串为“hello”,则此字符串的前缀包括“h”、“he”、“hel”、“hell”,我们将前缀所组成的集合称之为前缀集合(字符串本身不是自己的前缀,也不是自己的后缀)。此字符串的后缀包括“o”、“lo”、“llo”、“ello”,我们将后缀组成的集合称之为后缀集合。

再比如有一个字符串“abcdabc”,它的前缀集合为{“a”、“ab”、“abc”、“abcd”、“abcda”、“abcdab”},其后缀集合为{“c”、“bc”、“abc”、“dabc”、“cdabc”、“bcdabc”}。两个集合中重复的子串中长度最大的为“abc”。

 

普通算法代码如下:

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

int simple(const char *str1, const char *str2)//n*m
{
	assert(NULL != str1 && NULL != str2);

	int sit = 0;
	int i = sit;
	int j = 0;

	while (str1[sit] != '\0' && str1[i] != '\0' && str2[j] != '\0')
	{
		i = sit;
		j = 0;
		while (str1[i] != '\0' && str2[j] != '\0' && str1[i] == str2[j])
		{
			i++;
			j++;
		}
		if (str2[j] == '\0')
		{
			return i - j;
		}
		if (str1[i] == '\0')
		{
			return -1;
		}
		sit++;
	}
	return -1;
}

int main()
{
	char *str1 = "abcdabcabababcc";  //const char*
	char *str2 = "abcc";

	int sit = simple(str1, str2);
	printf("%d\n", sit);

	return 0;
}

      代码运行结果如下图所示:

 

 

        KMP算法代码如下:

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

int KMP(const char *str1, const char *str2)
{
	assert(NULL != str1 && NULL != str2);

	int *next = (int*)malloc(sizeof(int)*strlen(str2));
	assert(NULL != next);

	getNext(str2, next);

	int i = 0;
	int j = 0;
	int len = strlen(str2);
	while (str1[i] != '\0' && j < len)
	{
		if (j == -1 || str1[i] == str2[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j == len)
	{
		return i - j;
	}
	return -1;
}

void getNext(const char *str, int *next)
{
	assert(NULL != str && NULL != next);

	//int len = strlen(str);
	next[0] = -1;//因为搜索时不可以从0位置往前搜索了,即使没有找到。
	int i = -1;
	int j = 0;

	while (j <= strlen(str))
	{
		if (i == -1 || str[i] == str[j])
		{
			i++;
			j++;
			next[j] = i;//next里面存放的是下一次搜索时的位置
		}
		else
		{
			i = next[i];
		}
	}
}


int main()
{
	char *str1 = "abcdabcabababcc";//const char*
	char *str2 = "abcc";

	int sit2 = KMP(str1, str2);
	printf("%d\n", sit2);

	return 0;
}

      代码运行结果如下图所示:

          相对于普通的字符串匹配算法,KMP算法已经是非常优秀的了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章