1. KMP 实际上相对于Boyer Moore算法来看,实际上只使用了一个启发性算法,那就是找公共子串(左边是以开头为前缀,右边是以最后一个字符为后缀),通过这个公共子串就可以减少一些不必要的比较.
2. 我觉得大部分人应该也和我一样一开始最搞不懂的就是shift表怎么求的吧?
实际上网上很多解释都没有把一些事情用白话说清楚, 因为你上来就Next数组,i,j啊啥的,其实很多小伙伴就蒙了。。。。
那么我这里先定义一下,我这个版本的数组的内容是:
Array[i]: 指的是在一个pattern串里从0到下标为i的字符(包括Pattern[i]这个字符)形成的字符串里,
最长的公共子串: 这个公共子串指的是以pattern[0]为前缀,和以pattern[i]为后缀的字符串的最长公共子串
以abcabc为例子
Array[4]=2;
这里的最长公共子串就是abcab
标黑的部分
实际上等我们把这个计算出来了,接下来就是算在i位置上匹配失败时应该移动多少距离: 已经匹配成功的字符数-Array[i].
其实就是先算一下公共子串长度,然后利用这个信息再算实际要怎么移动!简单吧?
那么我们其他地方就不提直接看Array怎么算吧?
直接上代码:
void CalPrepArray(string & pattern, int* Array) { int m=pattern.length(); int *tmp=new int[m];
//第一个字符一定是0 Array[0]=0;
//如果pattern只有一个字符, 直接就可以知道最后结果 if(m==1) { Array[0]=1; return; } int k=0; int i=1;
// 默认上一次的最长结果为0
//i从1开始数, 0已经计算过了 while(i<=m-1) { if(k>=0&&pattern[k]==pattern[i]) {
//发现有之前的结果可以利用 Array[i++]=++k; } else if(k==0&&pattern[k]!=pattern[i]) {
// 避免k=0的时候Array[k-1[访问越界 Array[i++]=0; k=0; } else {
//为啥这里是k-1你大概明白吧?k是上一次最长公共子串的长度Array是根据位置来算的所以才是k-1
k=Array[k-1]; } } //后面就不解释了 for(int i=0;i<m;i++) { tmp[i]=Array[i]; } for(int i=0;i<m;i++) { if(i==0||i==1) { Array[i]=1; } else Array[i]=i-tmp[i-1]; cout<<Array[i]<<endl; } delete[] tmp; }
很多人可能理解不了k的含义到底是啥?
其实就是上一次也就是Array[i-1]的结果. 因为我们发现Array[i]的值和上一次的最长公共串的结果有关.
其中可能不太好理解的就是回溯是怎么做的
懂了吧?是不是很简单, 剩下的东西我放源码注释里了,挺简单的.