1.KMP算法
KMP算法的匹配过程,可以分为两个个步骤:求next数组、根据next数组进行匹配。
现在给出一个主串T:abaabaabcabaabc,指向主串的指针 i;给出一个模式串S:abaabc,指向模式串的指针 j
步骤如下
1.1求next数组
next数组的第一项next[0] = -1(如果 j 以1为起始编号,那么next[0] = 0)。
j | 0 | 1 | 2 | 3 | 4 | 5 |
模式串 | a | b | a | a | b | c |
next[j] | -1 |
除next数组第一项外,next[j] = 第1个字符到第 j 个字符(不包括第 j 个字符)的前后缀最长匹配长度(当 j 的起始值为1时,next数组的值也要相应+1)。
前缀:除最后一个字符外,前任意个数的连续字符都是前缀。
后缀:除第一个字符外,后任意个数的连续字符都是后缀。
匹配:两个字符串相等,长度相等,每个对应的字符也相等,也就是strcmp(str1,str2)= 0。
对于模式串S:abaabc来说,它的所有前缀是:a、ab、aba、abaa、abaab;它的所有后缀是:c、bc、abc、aabc、baabc。
当 j 取1时,串为a:
没有前缀和后缀,因此最长匹配长度为0,next[j] 取 0
当 j 取2时,串为ab:
前缀取a,后缀取b,不匹配
因此最长匹配长度为0,next[j] 取 0
当 j 取3时,串为aba:
前缀取a,后缀取a,匹配,长度为1
前缀取ab,后缀取ba,不匹配
因此最长匹配长度为1,next[j] 取 1
当 j 取4时,串为abaa:
前缀取a,后缀取a,匹配,长度为1
前缀取ab,后缀取aa,不匹配
前缀取aba,后缀取baa,不匹配
因此最长匹配长度为1,next[j] 取 1
当 j 取5时,串为abaab:
前缀取a,后缀取b,不匹配
前缀取ab,后缀取ab,匹配,长度为2
前缀取aba,后缀取aab,不匹配
前缀取abaa,后缀取baab,不匹配
因此最长匹配长度为2,next[j] 取 2
更新next数组
j | 0 | 1 | 2 | 3 | 4 | 5 |
模式串 | a | b | a | a | b | c |
next[j] | -1 | 0 | 0 | 1 | 1 | 2 |
1.2进行匹配
(1)设定主串的指针 i 和模式串的指针 j 的起始值,这里为0。
(2)对 T[i] 和 S[j] 进行比较。
(3)如果匹配成功,如果 j 已经取得最大值,说明匹配成功;否则 i++,j++,回到(2)。
(4)如果匹配失败,如果 i 已经取得最大值,说明匹配失败;否则 j = next[j],i不变,回到(2)
2.KMP算法的优化
上面定义的next数组尚有缺陷,可以进一步优化。
假设主串T:aaabaaaab;模式串S:aaaab。
主串 | a | a | a | b | a | a | a | a | b |
模式串 | a | a | a | a | b | ||||
j | 0 | 1 | 2 | 3 | 4 | ||||
next[j] | -1 | 0 | 1 | 2 | 3 |
当 j = 3 时,匹配失败,根据next数组的值,应该令 j 取 2,于是下一次的匹配是S[2] = a 和 T[3] = b进行匹配且失败;再下一次是S[1],然后是S[0]。可以看到S[0]、S[1]、S[2]、S[3]是相等的,当S[3]匹配失败时,跳转的S[2]、S[1]、S[0]一定会发生失败,这些匹配都是无意义的。
当next指向的字符和当前字符相等,那么下一次的匹配必定是失败的,可以直接跳过,所以应该直接跳转到next的next。于是可以根据next数组,更新出新的跳转数组,命名为nextval。
2.1求nextval数组
以第一节的主串T:abaabaabcabaabc 和模式串:abaabc 进行分析
nextval数组的第一项nextval[0] = -1(如果 j 以1为起始编号,那么nextval[0] = 0)。
j | 0 | 1 | 2 | 3 | 4 | 5 |
模式串 | a | b | a | a | b | c |
next[j] | -1 | 0 | 0 | 1 | 1 | 2 |
nextval[j] | -1 |
除nextval数组的第一项外,nextval[j] 的取值有两种情况:当S[j]和其对应的next位置的字符相等(即S[j] 和 S[next[j]]相等时),nextval[j]的值取相比较的字符对应的nextval(即nextval[j] 取 nextval[next[j]]);如果不相等,则直接取对应next数组的值
当 j 取 1 时
S[1] = b,S[next[1]] = S[0] = a,不相等,所以nextval[1] = next[1] = 0。
当 j 取 2 时
S[2] = a,S[next[2]] = S[0] = a,相等,所以nextval[2] = nextval[next[2]] = nextval[0] = -1。
当 j 取 3 时
S[3] = b,S[next[3]] = S[1] = b,不相等,所以nextval[3] = next[3] = 1。
当 j 取 4 时
S[4] = b,S[next[4]] = S[1] = b,相等,所以nextval[1] = nextval[next[4]] = next[1] = 0。
当 j 取 5 时
S[5] = c,S[next[5]] = S[2] = a,不相等,所以nextval[5] = next[5] = 2。
更新nextval数组
j | 0 | 1 | 2 | 3 | 4 | 5 |
模式串 | a | b | a | a | b | c |
next[j] | -1 | 0 | 0 | 1 | 1 | 2 |
nextval[j] | -1 | 0 | -1 | 1 | 0 | 2 |
2.2进行匹配
这里的匹配方法和1.2是相同的,无非是将其中的next数组,替换为nextval数组