這個算法經常忘記,今天看了記錄一下心得,自留用。
講得比較好的:
字符串匹配算法KMP詳細解釋——深入理解
講的比較詳細的:
字符串匹配KMP算法的理解(詳細)
簡潔易懂版本:
轉載一篇單字符串匹配KMP算法最好理解的文章
思路:
一般都先講暴力匹配,這種情況下,每次都從字符串開頭開始匹配,當子串長M,父串長N,複雜度是O(M*N)
匹配串的時候,例如:ABCDABD
,每次都從頭開始匹配,每次都要回溯子串
到最初,這樣沒有利用已經匹配上的信息,例如:匹配到第二個B時失配
,我們知道父串前面有ABCDA
,所以如果從頭開始匹配,可以直接從B開始匹配,因爲知道前面已經有一個A了。 這樣就利用了已知信息。
爲了得到這樣的信息,需要生成下面的next表。
A從子串首字符開始匹配,B從第二個字符開始匹配,其他的由於沒有重複的結構,只能從頭暴力匹配了。
如何生成next表:
從上面的描述我們知道,next表生成只與子串相關,與父串沒關係。
如何使用next表:
用i,j標識父串和子串目前匹配到的位置,
現在每次需要回溯的距離=目前子串匹配到位置-next[i]
例如:BBC ABCDAB ABCD
ABCDABD
此時匹配到D,不匹配了,則 i=i+4 且 j=2。
例如:BBC ABCDAB ABCD
ABCDABD
子串和父串的匹配進度都前進了一大截,節省了(2*子串長+2)
次匹配。
代碼:
(c代碼是別人擼的,我就照着寫了下思考了一下,下面的python是我寫的)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
void print(int a[],int len)
{
int i=0;
while(i<len)
{
printf("%d ",a[i]);
i++;
}
printf("\n");
}
void prints(char a[],int s,int e)
{
int i=s;
while(i<e)
{
printf("%c",a[i]);
i++;
}
printf("\n");
}
void cal_next(char * str,int * next,int len)
{
int i,j;
next[0]=-1;
for(i=1; i<len; i++)
{
j=next[i-1];
while(str[j+1]!=str[i]&&(j>=0))
{
j=next[j];
}
if(str[i] == str[j+1])
{
next[i]=j+1;
}
else
{
next[i]=-1;
}
}
print(next,len);
}
int KMP(char * str,int slen,char* ptr,int plen,int *next)
{
int s_i=0,p_i=0;
while(s_i<slen &&p_i<plen)
{
if(str[s_i]==ptr[p_i])
{
s_i++;
p_i++;
}
else
{
if(p_i==0)
s_i++;
else
p_i=next[p_i-1]+1;
}
}
return (p_i==plen)?(s_i-plen):-1;
}
int main()
{
char str[N]= {0};
char ptr[N]= {0};
int slen,plen;
int next[N];
while(scanf("%s%s",str,ptr))
{
slen=strlen(str);
plen=strlen(ptr);
cal_next(ptr,next,plen); //計算next數組
printf("%d\n",KMP(str,slen,ptr,plen,next));
}
return 0;
}
python:
# KMP算法
string,strp=input().split(",")
next=[0 for n in range(0,len(strp))]
for i in range(0,len(strp)):
if i==0:
next[0]=-1
continue
j=next[i-1]
while(strp[j+1]!=strp[i] and (j>=0)):
j=next[j]
if(strp[i]==strp[j+1]):
next[i]=j+1
else:
next[i]=-1
print(next)
s_i=0
p_i=0
while(s_i<len(string) and p_i<len(strp)):
if(string[s_i]==strp[p_i]):
s_i+=1
p_i+=1
else:
if(p_i==0):
s_i+=1
else:
p_i=next[p_i-1]+1
if i==len(strp):
print(i)
else:
print(-1)