kmp算法是对字符串暴力匹配算法的优化,主要通过next数组记录最大的相等的前缀和后缀长度进行优化。
KMP原理
理解kmp算法首先要知道前缀和后缀两个概念,前缀 是除了最后一个字符外字符串所有的头部子串,后缀 是除了第一个字符外字符串所有的尾部子串,例如"ababa"的前缀就是{a,ab,aba,abab}后缀就是{a,ba,aba,baba}。在字符串匹配过程的中,如果模式串的前j个字符与主串的a~a+j字符匹配,第j+1个字符与a+j+1不同,而且模式串前j个字符的最大相同前后缀长度是x即模式串的0~x-1与j-x+1~j相同那么第j+1个字符与a+j+1不同时就不需要从模式串的第0个字符开始匹配而是从第x个字符匹配,从而减少了匹配时间,这就是kmp算法通过记录模式串最大相同前缀后缀长度减少匹配时间的方式。
next数组
从kmp算法的原理中不难看出最重要的部分就是求最大相同前后缀长度也就是next数组,我个人理解的next数组是求模式串前j字符最大前后缀长度,那么在字符串匹配的过程中第j+1个字符不匹配,模式串就只需要从第next[j]个字符开始重新匹配j+1=next[j](坐标从0开始长度是next[j]),也有的是next数组是next[j]表示前j-1个字符的最大前后缀长度,那么i=next[j]。前n-1个字符的最大相同前后缀和是j-1,如果j==i则next[i]=j,如果不同则比较next[j-1],因为next[j-1]=x可以保证 【0,x-1】=【i-x+1,i】而且若next[i]=a,则【0,a-1】=【i-a+1,i】,【0,a-2】=【i-a+1,i-1】又因为next[i-1]=b=j-1,则【i-a+1,i-1】=【b-a+2,b】=【0,a-2】则next[b]>=a-1,即next[i]一定在next[j-1]中,所以用next[j-1]可以确定是i的最大相同前后缀长度求next数组的时候就是在用kmp,把 【0,j】 的前缀看成是模式串,把 【1,i】 的后缀看成是文本串,①文本串一直是每次循环+1,不会回退 ②模式串有时候可以通过next数组跳过前面几个字符直接比较。
代码实现
cpp
#include<string>
#include<vector>
#include<iostream>
using namespace std;
void GetNext(string& s,vector<int>& next)
{
int i = 1;
int j = 0;
next[0] = 0;
for (; i < next.size(); i++)
{
if (s[i] == s[j])
{
next[i] = ++j;
continue;
}
while (s[i] != s[j]&&j>0)
{
j = next[j - 1];
}
if (s[j] != s[i]) next[i] = 0;
else next[i] = ++j;
}
}
int Index(string& s, string& t)
{
vector<int> next(t.size(), 0);
GetNext(t, next);
int i = 0;
int j = 0;
while(i<s.size()&&j<t.size())
{
if (s[i] == t[j])
{
i++;
j++;
}
else if (j == 0)
{
i++;
}
else
j = next[j - 1];
}
if (j == t.size())
return i - t.size();
else
return -1;
}
int main()
{
string s = "anssna";
string t = "sna";
cout << Index(s, t);
return 0;
}