next数组是在kmp算法中用于目标字符串回溯时使用的,类似于给目标字符串中每个字符一个特征值,方便回溯,而不是每次都只是单纯的向后移动一位的暴力对比,可以极大的降低时间消耗。
我们首先要理解next数组是如何产生的
以”abcdaabcab“为目标“abcddabcababcdaabcababcdaabcabaa”为主串为例,当为如下情况:
abcddabcababcdaabcababcdaabcabaa abcdaabcab
在第五个字符,也就是i=j=4时匹配出错那么我们就看j=4时对应的next值,然后让j=next[j],这个时候再用此时的j所对应的字符开始去与i=4时的字符继续比较(干说不好理解,先来点实际的)
预先告诉abcdaabcab这个东西对应的next为-1 0 0 0 0 1 1 2 3 1 (别问我咋来的这么快,一会儿教)那么j=next[j]=0,那么我们就直接吧编号为零的直接拽过来就行了
abcddabcababcdaabcababcdaabcabaa abcdaabcab
前面几个都不用看的,看了也是无用功,总之就是这么用的
然后我们来说一说这个next是咋来的,其实很简单,就是看当前字符前面的字符串首尾有几个相同顺序的(用人话说不清楚)
abcdaabcab 这个红a,前面是abcd 首尾没有相同的所以这个a对应的next为0
abcdaabcab再看这个a,前面是abcda 俩a首尾照应所以这个a对应的next为1
abcdaabcab看这个c,前面是abcdaab ab两字符首尾照应,所以这个c对应的next为2
同理 ,abcdaabcab,这个a对应的就是3啦(abcdaabcab)
注意:所找首尾不能重合!!!(比如cbcbca a对应的next为1,而不是3)
这个其实就是找当前这个字符前有几个和开头时是一样的,这样的情况把开头的位置直接移过来也会有最大的可能性匹配成功,毕竟就是在这个位置才不一样的,比如:
在“abcddabcababcdaabccaabcababcabaa”中找“abcdaabcab“(注意主串和上面说的不一样)
有一种情况
abcddabcababcdaabccaabcababcabaa abcdaabcab 此时在标红处不同,红a对应的next为3那就把序号 abcdaabcab 为3的拽过来,只用一步这不就匹配对了嘛
根据这个思路我们就可以写出初始版的getnext函数
不优化的next数组
int* getnext(string aim,int next[])
{
int k=-1;
int j=0;
next[0]=-1;//规矩 方便后面k与j的逐渐分离进行比较,赋值(记着就好)
while (j < aim.size()) //这个很容易理解作为目标字符串的指针肯定不能跑出去
{
if (k==-1||aim[j] == aim[k])
{
j++;
k++;//肯定要+1,不然aim[k]就没意义了
next[j] = k;
}
else
{
k = next[k];
}
}
}
这里比较好理解,k其实就是负责记录首尾有几个相同字符,j就是记录next数组记到哪了,观察可以发现j是只加不减,而k会因为k=next[k]一战回到解放前的,(具体可以自己一步一步手写去记录每一步j与k的值的变化,然后再改变目标串个别字符,比如把abcdaabcab中倒数第二个a换成d或者倒数第三个c换成b,去注意k的变化,自有一番天机)
缺点 还会有一定程度的无效配对,比如
abcddabcababcdaaccababcdaabcabaa abcdaabcab 当这种情况时,因为b的next是1对应的依然是b,移动相当于有一定的浪费,所以可以进行一定的优化,保证都是有效移动
优化后的getnext
int* getnext(string mubiao, int next[])
{
int k = -1;
int j = 0;
next[0] = k;
while (j < mubiao.size())
{
while (k >= 0 && mubiao[j] != mubiao[k])
k = next[k]; //k的意义有所不同,一直保持的是重复段的next
j++;
k++;
if (mubiao[j] == mubiao[k])
{
next[j] = next[k];
}
else
{
next[j] = k;
}
}
}
对于字符串”abcdaabcab“
在优化后的程序中运行
每一步进行的数据变化
k=-1 j=0 next[j]=-1 0 1 next[1]=0 -1 0 2 next[2]=0 -1 0 3 next[3]=0 -1 0 4 next[4]=-1 1 5 next[5]=k=1 0 1 6 next[6]=next[1]=0 2 7 next[7]=next[2]=0 3 8 next[8]=k=3 0 1 9 next]9]=next[1]=0优化后的程序对于字符串”abcdaabcab“便可以利用k来记录当前位置以及前面的位置是否与开头部分相同,从而确定是使next[j]=k 还是next[k],等于next[k]其实就是保证有效的移动
(最后一点优化部分写的不是很好,后面我会继续完善,也请大神指点)