-------咬定青山不放松,立根原在破岩中
大家好,周末在家学习KMP算法,却卡在了生成部分匹配表这块,不知道大家对这个算法理解的怎么样,反正我是看了半天没看明白ψ(*`ー´)ψ✄╰ひ╯,但是!!!,经过我不懈的努力与持之以恒的奋斗以及上天的眷顾。。。。还是没看出来(_(:3」∠❀)_菊花碎了一地),可就在我躺在床上一筹莫展时,灵光突然一现,所以我抓紧写篇文章记录一下先。
好的,废话不多说,代码如下:
public static int[] getPartTable(String str) {
//部分匹配表数组
int[] table = new int[str.length()];
//第一个设置为 0
table[0] = 0;
// i用来指向部分匹配字符串末尾的字符;
// j用来指向开始的字符;
for (int i = 1, j = 0; i < str.length(); i++) {
// 当j>0且前缀后缀不匹配时,使用部分匹配表中前一个表项的值
while (j > 0 && str.charAt(j) != str.charAt(i)) {
j = table[j - 1];
}
//如果前缀后缀匹配,j向后移,继续比较
if (str.charAt(j) == str.charAt(i)) {
j++;
}
table[i] = j;
}
return table;
}
个人认为,这段代码最难理解的是下边这一句:
// 取前一个表项的匹配值
j = sectionTable[j - 1];
下面我会带大家针对模式串进行手动推演,估计大家看到最后,也就理解的差不多了。
随便举一个例子:str = "ABABAAABA"; 字符串长度为9,所以要定义一个长度为9的数组用来保存部分匹配表为T;前后缀最大公共长度推导过程如下:
index | T | str |
0 | 0 | A |
1 | 0 | AB |
2 | 1 | ABA |
3 | 2 | ABAB |
4 | 3 | ABABA |
5 | 1 | ABABAA |
6 | 1 | ABABAAA |
7 | 2 | ABABAAAB |
8 | 3 | ABABAAABA |
①部分串"A"就一个字符,所以前后缀最大公共长度为0;所以T[0] = 0;
②部分串 "AB" 在①的基础上加了一个字符"B", 此时 i = 1, j = 0; 判断出str.charAt(j) != str.charAt(i),所以j = T[i] = j = 0;
③部分串 "ABA" 在②的基础上加了一个字符"A", 此时i = 2, j = 0; 判断出str.charAt(j) == str.charAt(i),所以j++,T[i] = j = 1;
④部分串 "ABAB" 在③的基础上加了一个字符"B", 此时i = 3, j = 1; 判断出str.charAt(j) == str.charAt(i),所以j++,T[i] = j = 2;
⑤部分串 "ABABA" 在④的基础上加了一个字符"A", 此时i = 4, j = 2; 判断出str.charAt(j) == str.charAt(i),所以j++,T[i] = j = 3;
⑥部分串 "ABABAA" 在⑤的基础上加了一个字符"A", 此时i = 5, j = 3 判断出str.charAt(j) != str.charAt(i),取上一个表项的值,也就是 j 对应的字符"B"前边"ABA" 的匹配值,也就是取③中匹配出来的值 j = T[j -1] = T[2] = 1, 此时while条件成立,str.charAt(j) != str.charAt(i),继续取之前匹配的值 j = T[j -1] = T[0] = 0,判断出 str.charAt(j) == str.charAt(i),所以j++, T[i] = j = 1;
⑦部分串 "ABABAAA" 在⑥的基础上加了一个字符"A", 此时i = 6, j = 1; 循环判断出str.charAt(j) != str.charAt(i),继续取之前匹配的值 j = T[j -1] = T[0] = 0,判断出 str.charAt(j) == str.charAt(i),所以j++, T[i] = j = 1;
⑧部分串 "ABABAAAB" 在⑦的基础上加了一个字符"B", 此时i = 7, j = 1 判断出str.charAt(j) == str.charAt(i),所以j++,T[i] = j = 2;
⑨部分串 "ABABAAABA" 在⑧的基础上加了一个字符"A", 此时i = 8, j = 2 判断出
str.charAt(j) == str.charAt(i),所以j++,T[i] = j = 3;
最后得到部分匹配表:T = [0, 0, 1, 2, 3, 1, 1, 2, 3];
总结
部分匹配表的生成可以分为两部分,一是在原有的基础上累加,例如③④⑤,是在原有基础上进行的最大长度匹配;如果字符不相等,则取上一个表项中的值,也就是第0位到第 j - 1 位字符的最大长度,查找过程与动态规划类似。