kmp是經典的單模式串字符串匹配算法,對於一個字符串在長文本中的匹配很有效。
kmp算法包括兩部分,對模式串的預處理和模式串匹配**
1、模式串預處理
這一部分關鍵在於next數組的構造,相當於對模式串進行kmp匹配。注意,字符串數組與next數組的首字符不存儲信息。
next數組中儲存當該字符的下一個不匹配時,下一步應當跳轉到哪裏,即,到此爲止該字符串的最長公共前綴,後綴的長度。
詳細見代碼註釋
void next(int *next, char *c)//字符串從c[1]開始賦值
{
//k爲要比較的字符的前一個的下標,一開始比較c[1]c[2],所以k初始化爲0
int k = 0;
//首字符的next值初始化爲0,說明包①含這個字符的字符串前後綴長爲0,②即要比較的字符c[1]的前一個
next[1] = 0;//注意next數組值的兩層含義
//一開始比較c[1]c[2],i初始化爲2,
for (int i = 2; i < strlen(c); i++)//next數組最後一個的值比strlen值小一,所以不要取等
{
//當k可以回溯且不匹配時,讓k回溯
//k初始化爲0,保證可回溯,即k>0
while (k > 0 && c[i] != c[k + 1])
{
k = next[k];//對前串來說a[k]是a1中前綴的最後一個字符所在位置
}
if (c[i] == c[k + 1])//若匹配則k+1,
k++;
next[i] = k;//包含i的字符串的前綴長=加過1的k。
}
}
2、kmp匹配部分
原理與模式串匹配相似,只不過是兩個字符串的匹配
void kmp(char *t, char *p)//t爲長串,p爲短串
{
int *next;
next = (int *)malloc(sizeof(int)*(strlen(p)+1));
buildnext(next, p);
int k = 0;//k爲要比較的字符的前一個的下標,一開始比較模式串中的c[1],k初始化爲0
for (int i = 1; i < strlen(t); i++)//長串的最後一個字符的下標爲長度-1
{
while (k>1 && p[k + 1] != t[i])
k = next[k];
if (p[k + 1] == t[i])
k++;
if (k == strlen(p)-1)
{
printf("開始下標爲%d\n", i - strlen(p)+1+1);
k = next[k];//尋找下一個匹配串
}
}
這種原始的kmp算法有一個弊端,當字符串重複性很高時(例如ababababag),它的效率和樸素匹配沒啥區別了。因爲當重複性很高時,如果有一位失配,那麼再向後移動仍然失配。