用KMP算法的前提是要有前缀表,那么如何计算前缀表呢?我们以字符串 str = “aaabbaaaa"为例。
前缀:必须包含第一个字母但不包含最后一个字母的连续子串
后缀:必须包含最后一个字母但不包含第一个字母的连续子串
前缀表:最长相等前后缀(即这个字符串的前缀和后缀相等,还是最长的那个)
(为了相同的字母区别开来是第几个,采用不同的颜色表示)
这是我们用眼瞅出来的,代码怎么写呢?
首先我这里定义 i 为前缀末尾,j 为后缀末尾(个人习惯 i 在前,但我可能和大部分是反着定义的,但是这不重要)
定义一个next数组用来记录前缀表,初始化为0;
由于前一个str[0]必然无前缀后缀,所以next[0]必然为0,已在初始化时初始好了,所以我们直接从前两个开始
首先明确一下,上图情况是在i从0,j从5开始一直向后匹配出现的不相等的情况,即下标所指处:0和5处相等,1和6处相等,2和7处相等,但3和8处不相等
既然是i从0,j从5开始,那么下一次比较就应该是i从0,j从6开始,匹配结束后next[8]=3,这就是在重复比较
i的前一位的next数组值记录了此时子串的最长相等前后缀:
先看紫色的,再看黄色的,再看绿色的,再看红色的(文字的局限型()
先看紫色的,再看黄色的,再看绿色的,再看红色的(文字的局限型()
同样实现了,相当于上面i从0,j从6的匹配过程,但是前两个已经知道匹配结果是相等了,就不用重复匹配了
因为前一位的next数组值记录了该子串的最长相等前后缀,那么我们只需要从最长相等的前缀的下一位开始进行比较就可以(结合上图来理解),而由于数组的下标从0开始,个数从1开始,所以next数组所记录的个数刚好就是最长前缀下一位的下标
#include<iostream>
#include<string>
using namespace std;int main()
{string s = "aaabbaaaa";int next[9] = { 0 };for (int i = 0, j = 1;j < s.size();j++){while (i > 0 && s[i] != s[j])//大于0是回退到开头就不用在回退了{i = next[i - 1];}if (s[i] == s[j]){i++;next[j] = i;//这里与在理论时我说的是先更新next数组值,即先:next[j] = i+1;//然后:i++;//其中j++写在for循环表达式里了//是一样的}}for (const auto& it : next)cout << it << ' ';//打印出来看一下嘿嘿
}
以上,希望对你有所帮助,KMP真是头疼啊()
更新:有个小问题,代码那
if (s[i] == s[j])
{i++;next[j] = i; //如果是这样把这个句放在if语句里,那么只有相等的时候才会更新next数组//那不相等的时候next数组就没更新,直接被跳过去了,但是由于初始化全是0,所以被跳过去也依然正确,因为本来就该是0//所以换成先更新next数组再i++的写法也不会出问题,都是初始化为0的功劳
}
if (s[i] == s[j])
{i++;
}
next[j] = i;//如果把这句拿出来,就是不管初始化成什么也不会出错,当然第一个必须初始化为0//因为如果i回退到0了还不相等,i就等于0,没有++,所以next的更新完全正确//所以以这个为标准好一点//但是拿出来就不能先更新next数组值再i++了//所以还是先i++比较好
本文发布于:2024-01-31 07:59:51,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170665919226933.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |