nextval数组是在next数组缺陷的情况下提出的,例如:模式“aaaab”在和主串"aaabaaaab"匹配时,当i=4,j=4时s.ch[i]!=T.ch[j],由于next[j]=3的指示,会模式串右滑一位进行i=4,j=3的比较,接着有根据next数组的指示会进行i=4,j=2,i=4,j=1的比较。实际上,因为模式串1~3个字符和第四个字符都相等,因此不需要再和主串中第第四个字符比较,而可以将模式串连续向右滑4个字符的位置直接进行i=5,j=1的比较。
因为next存在上述的问题,所以提出了next数组的修改值——nextval数组,nextval数组的计算方法有两种:一种是不依赖于next数组直接观察得到的,另一种是根绝next数组进行推了得到的。
首先来分析以下第一种方法:
那“aaaab”做模式串为例:
1、如果第一个字符‘a’在于主串匹配时与主串不匹配则不需要将模式串进行任何滑动直接让主串下一个位置和模式串第一个位置比较就可以了。所以第一个位置'a'的nextval[1]=0;
2、如果模式串第二个字符'a'在与主串匹配是与主串不匹配,则说明主串当前字符一定不为'a',同时也说明主串的上一个字符一定与模式串的第一个字符匹配一定为‘a’,所以无论模式串怎么滑动,都不可能与主串当前字符匹配(因为主串当前字符不为'a',而模式串前面的字符都为'a'),所以只能主串下移一位重新与模式串第一位开始比较,所以模式串第二个位置'a'的nextval[2]=0。
3、与2理论相同得到第三位nextval[3]=0
4、与2理论相同得到第四位nextval[4]=0
5、如果模式串第五位‘b’与主串当前字符不匹配,则说明主串当前位置的字符不为'b',同时也说明主串前面四位都为‘a’,因为主串当前位置不为‘b’,则说明主串当前位置可能为'a',又因为模式串前四位都为‘a’,所以可以把模式串向右滑一位使模式串第四位‘a’与主串当前位置字符相比较。因为主串当前位置的前4位都为‘a’,说明主串的当前位置的前三位与模式串的前三位相同,所以模式串第五位置'b'的nextval[5]=4.
1 | 2 | 3 | 4 | 5 | 6 |
a | a | a | a | * | * |
a | a | a | a | b | |
a | a | a | a | b |
再举一个例子:“abaabcac”
1、如果模式串第一位'a'与主串当前字符不匹配,则不需要滑动了,直接主串后移一位与模式串第一位比较即可.所以模式串第一位'a'的nextval[1]=0。
2、如果模式串第二位‘b’与主串当前字符不匹配,则说明主串当前字符不为'b',主串当前字符的前一位一定与模式串第一位匹配为'a'。主串第二位不为'b',则可能为'a',所以可以将模式串向有滑动一位,让主串当前位置与模式串第一位进行比较,因此模式串第二位‘b’,nextval[2]=1.
3、如果模式串第三位‘a’与主串当前位置字符不匹配,则说明主串当前位置字符不为'a',同时也说明主串当前位置的前两个字符与模式串的前两个字符匹配位“ab”。因为第三个字符不为'a',则说明第三个字符可能位'b',如果为'b'则可以让模式串想右滑动一位让模式串第二位与主串当前位置比较,但是这样以来模式串第一位'a'就会与主串前一位'b'进行比较,明显不匹配,所以模式串这样滑动不可以,可以看到模式串前面两位'ab"不论怎么样都不能与主串当前位置匹配,所以只能让主串下移一位与模式串第一位开始比较。所以模式串第三位'a'的nextval[3]=0.
4、如果模式串第四位‘a’与主串当前位置字符不匹配,则说明主串当前位置不为'a',同时也说明主串当前位置的前三个位置与模式串前三个匹配为"aba"。因为主串当前位置不为'a',所以可能为'b',如果为'b',可以让模式串向右滑动两位,使模式串第二位‘b’与主串当前位置进行比较,这样模式串的第一个位置会和主串当前位置的前一个比较,显然都为'a',匹配。所以模式串第四位‘a’的nextvak[4]=2。
5、如果模式串的第五位‘b’与主串当前位置不匹配,则说明主串当前位置字符不为'b',同时也说明主串当前位置的前四位与模式串的前四位匹配人,为“abaa”。因为主串当前位置不为'b',所以可能为‘a’.如果为‘a’,则可以让模式串向右滑动一位,使模式串第四位与主串当前字符比较,但是此时还需要模式串前三"aba"位与主串当前位置的前三位“baa”匹配,显然是不匹配的所以这样滑动不可以;还可以模式串向右滑动两位,让模式串第三位与主串当前位置进行比较,但是此时模式串前两位'ab'与主串当前位置的前两位‘aa’不匹配,所以这样滑动也不可以;因为主串当前位置不为'b',所以不能右滑三位让模式串第二位与主串当前位置比较;模式串右滑四位,让模式串第一为'a'与主串当前位置比较,模式串前没有字符了,所以这样滑动可以。综上,模式串第五位‘b’的nextval[5]=1。
6、如果模式串的第六位'c'与主串当前位置不匹配,则说明主串当前位置不为‘c’,同时也说明主串当前位置的前五个字符串与模式串的前五个字符匹配为"abaab"。因为第六个字符不为'c',所以可以为‘a’或者'b'。则可以将模式串右滑一位,是模式串第五位与主串当前位置比较,但是此时模式串的前四个字符"abaa"与主串当前位置的前四个字符"baab"不匹配,所以不可以这么滑动;所以模式串可以向右滑动两位使模式串第四位与主串当前位置进行比较,但是这样的话模式串前三位“aba”与主串当前位置的前面三位“aab”不匹配,所以不能这么滑动;可以模式串向右滑动三位,让模式串第三位‘a’与主串当前位置进行比较,这时我们发现,模式串前两位"ab"与主串当前位置的前面两位“ab”匹配,所以该模式串的第六位‘c’的nextval[6]=3。
7、如果模式串第七位‘a’与主串当前位置的字符不匹配,用以上的方法可以得出nextval[7]=0
8、如果模式串第八位‘a’与主串当前位置的字符不匹配,用以上的方法可以得出nextval[8]=1
----------------------------------分割线------------------------------
接下来我们分析一下第二种方法:
模式串 | a | b | a | a | b | c | a | c |
next值 | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
nextval值 | 0 | 1 | 0 | 2 | 1 | 3 | 0 | 2 |
1、第一位netxval的值必为0.
2、第二位next[2]=1,这是比较第二位字符与第一位是否相,不同则nextval[1]=next[1]=1
3、第三位next[3]=1,这时比较第三个字符与第一个字符是否相同,均为a,相同,则,第三位nextval[3]=nextval[1]=0
4、第四位next[4]=2,这时比较第四位与第二位,不同,则第四位nextval[4]=next[4]=2
5、第五位next[5]=2,这时比较第五位与第二位,均为a,相同,则nextval[5]=nextval[2]=1
6、第六位next[6]=3,这时比较第六位与第三位,不同,则nextval[6]=next[6]=3
7、第七位next[7]=1,这时比较第七位与第一位,相同,则nextval[7]=nextval[1]=0
8、第八位next[8]=2,这时比较第八位与第二位,不同,则nextval[8]=next[8]=2。
综上:第二种方法就是依赖于next数组,比较模式串当前位置的字符与模式串当前位置next数组值指向的位置的字符,如果相等则该位置的nextval值就等于next数组指向位置的nextval值,如果不相等,该位置的nextval值,就等于该位置的next值。(注:第一位的nextval值为0)
无论是第一种方法求出来的nextval数组还是第二种方法求出来的nextval数组,只要统一模式串就都想同。
算法代码:
void get_nextval(SString T,int nextval[]){
//计算模式串T的nextval数组
int i=1;
nextval[1]=0;
int j=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){ //判断出next数组的值
++i;++j; //这是在计算next数组,next[i]=j
if(T.ch[i]!=T.ch[j]) //这里的j就是next[i],判断ch[i]==ch[j]
nextval[i]=j; //如果不等那么nextval[i]的值就是next[i]即j
else
nextval[i]=nextval[j]; //如果相同那么,nextval[i]的值就是
//nextval[next[i]](即nextval[j])的值
}
else
j=nextval[j]; //计算next数组
}
}