第一步:求next数组
为了方便理解,我们假设数组下标从1开始。在这种条件下,next[k] 记录了模板串s[ :k-1]的最长相等前后缀的长度+1。我们设定两个变量 j 和 i ,其中,j 是指向前缀末尾的指针,同时也记录了最长相等前后缀的长度+1;i 用于模板串s的遍历,同时也指向了其后缀末尾。
首先初始化next数组,next[1] = 0,j = 0;i 从1开始遍历模板串:
1.如果当前 j==0或s[i]==s[j],说明匹配成功,i++,j++(记录当前最长相等前后缀长度+1),然后将该长度 j 赋值给 next[i]
2.如果匹配失败,则利用next数组进行回退 j = next[j]
代码如下:
//数组下标从1开始
void getNext(string s, int* next)
{
i = 1, j = 0;
next[1] = 0;
while(i<s.length)
{
if(j == 0 || s[i] == s[j])
{
i++;j++;
next[i] = j;
}
else
j = next[j];
}
}
下标从0开始的代码如下:
在这种条件下,next[k] 记录了模板串s[ :k-1]的最长相等前后缀的长度。
//数组下标从0开始
void getNext(string s, int* next)
{
i = 0, j = -1;
next[0] = -1;
while(i<s.length)
{
if(j == -1 || s[i] == s[j])
{
i++;j++;
next[i] = j;
}
else
j = next[j];
}
}
第二步:匹配字符串
与求next数组的逻辑类似,直接看代码:
//数组下标从1开始
int kmp(string S, string s)
{
int i = 1, j = 1;
int next[s.length()+1];
getNext(s, next);
while(i<=S.length() && j<=s.length())
{
if(j==0 || S[i] == s[j])
{
++i;++j;
}
else
{
j = next[j];
}
}
if(j > s.length())
return i - s.length();//返回下标
else
return 0;
}
//数组下标从0开始
int kmp(string S, string s)
{
int i = 0, j = 0;
int next[s.length()];
getNext(s, next);
while(i<S.length() && j<s.length())
{
if(j==-1 || S[i] == s[j])
{
++i;++j;
}
else
{
j = next[j];
}
}
if(j == s.length())
return i - s.length();//返回下标
else
return 0;
}