什麼是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算法已經是非常優秀的了。