在学习字符串匹配的过程中接触到了kmp算法,在看过许多文章之后,发现许多文章对傻瓜作者不太友好,担心遗忘,在此做出傻瓜式讲解(不讲解算法原理,只记录作者求学之路上的坎坷难点
注:next数组求的是模式串的即目标子串,通俗点说是那个短的
kmp算法的核心在于求next数组的值,首先话不多说先贴代码
void Getnext(int next[],string t)
{
int j=0,k=-1;
next[0]=-1;//点一
while(j<t.length()-1)
{
if(k == -1 || t[j] == t[k])
{
j++;k++;//点二
next[j] = k;//自加后再赋值因为自加前的字符已经相等,如果下一对不一样则回溯到前面再比较这一对不匹配的字符看是否能匹配
}
else k = next[k];//点三
}
}
首先我们看到代码看点一处,设置next[0]为-1;这个地方我一开始并不理解,但在日常写代码时,为了各种方便也会将变量初始值设为-1,而在此next求解时,后续next值是根据前面next值的大小确定的,因此可以抽象地将next数组看作一个变量,只不过将变量每次变化的值都记录下来,先暂且将看点一的疑惑搁置
到了点二处,在判断二者字符相同后,先自增再赋值,这一处在理解整体代码时相当重要,而这么做的原因是在判断该处字符相等后,可以直接决定如果下一字符匹配不成功返回最大相同前缀的该相等字符下一位置的指针.这也可以理解为什么初始值为-1,可以极大地方便后续求解.
点三则是最难点,乍一看实在是难以理解,这里叫做k的回溯,首先要发现,这里的k类似一个计数器,记录了从零开始有几个字符与前缀相同,知道这个之后,这里就不难理解了,把k回溯到next[k]意思是在主串这个不匹配的字符之前,已经有k个字符可以和匹配串的前k个字符相匹配了,不需要再额外匹配,只需要比对next[k]所代表的第k+1个字符就可以
]在其内可能还有小循环例如abcabdabcabd;
而此next求值法存在缺陷,很多教程也都提出了,直接贴出改进代码,
缺陷发生问题的原因在于t[j] == t[next[j]];
例如abab,当后一个b匹配失败时会回溯到第一个b但这是完全没有必要的;
void Getnext(int next[],string t)
{
int j=0,k=-1;
next[0]=-1;
while(j<t.length()-1)
{
if(k == -1 || t[j] == t[k])
{
j++;k++;
if(t[j]==t[k])//当两个字符相同时,就跳过
next[j] = next[k];//这里嗷,这里是改进的地方嗷嗷
else
next[j] = k;
}
else k = next[k];
}
}
完整的使用kmp算法
int KMP(String s,string t)
{
int next[MaxSize],i=0;j=0;
Getnext(t,next);
while(i<s.length()&&j<t.length())
{
if(j==-1 || s[i]==t[j])
{
i++;
j++;
}
else j=next[j]; //使用了next数组,开始发挥作用了!
}
if(j>=t.length())
return (i-t.length()); //匹配成功,返回子串的位置
else
return -1; //匹配失败
}