總結字符串匹配算法(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算法已經是非常優秀的了。

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