课本中的next函数如下:
void get_next(SString T,int next[])
{
int i=1;
next[1]=0;
int j=0;
while(i<T.length)
{
if(j==0||T.ch[i]==T.ch[j])
{
++i;
++j;
next[i]=j;
}
else j=next[j];
}
}
以T=aabaaa为例:
进入循环之前:i=1,j=0
进入循环之后:
第一趟:由于j==0,进入 if 语句i=2,j=1,next[2]=1
第二趟:由于T.ch[2](=a)==T.ch[1](=a) , i=3,j=2,next[3]=2
第三趟:由于T.ch[3](=b)!=T.ch[2](=a) , 进入else语句, 此时 j=next[2]=1
第四趟:由于T.ch[3](=b)!=T.ch[1](=a) , 进入else语句,此时 j=next[1]=0
第五趟:此时虽然T.ch[3](=b)!=T.ch[0],但是j==0,已然会进入if语句,此时i=4,j=1,next[4]=1
第六趟:此时T.ch[4](=a)==T.ch[1](=a),i=5,j=2,next[5]=2
第七趟:此时T.ch[5](=a)==T.ch[2](=a),i=6,j=3,next[6]=3
自此get_next函数结束,得到的next数组为:
j | 1 | 2 | 3 | 4 | 5 | 6 |
T | a | a | b | a | a | a |
next[j] | 0 | 1 | 2 | 1 | 2 | 3 |
不难发现i始终没有回溯,在第三趟时发现第三个字母与第二个字母不一样后,由j进行回溯,依次将第三个字母与第二个、第一个字母比对,在此过程中j回溯的方式由j=next[i]控制,这是因为此时next数组中next[1]已经有值且未进入循环之前已经定义了next[0]。此后有两种情况:
1)若前两个字母始终与第三个字母不一样,则会像上述一样由j=0再次进入if语句。
2)若前面有字母与第三个字母一样,这里我们假如第一个字母为b,则第四趟应为:T.ch[3](=b)==T.ch[1](=b),进入if语句,i=,j=2,next[4]=2
通过前面的分析可以看出来next函数的目的是寻找i指向字母之前的串的相同最大真前缀和最大真后缀+1的长度的值
j | 1 | 2 | 3 | 4 | 5 | 6 |
T | a | a | b | a | a | a |
最大真前缀 | 无 | 无 | a | 无 | a | aa |
next[j] | 0 | 1 | 2 | 1 | 2 | 3 |
注:aabaa的真前缀有:a,aa,aab,aaba;真后缀有:a,aa,baa,abaa
为什么是最大真钱后缀的值加1?
因为当时,其实就是在
个字母之前有相同的最大真前后缀,而其长度为k-1,所以要表示它之后的数就加1。
综上所述,next函数的本质为求相同最大真前后缀的值加1,其就为主串要移动的值,而在next函数中,T既是主串又是模式串。