字符串匹配中的KMP算法
*以此谨记自己学习Java心得
刚刚琢磨完KMP算法,老师的视频和代码看了一次又一次,虽然不能从真正底层原理上理解,但是对于应用这个算法是熟悉了。KMP算法用与匹配两个字符串是否有一样的值,在这里将S1父字符串设置为"BBC ABCDAB ABCDABDDBADC",S2子字符串设置为"ABCDABD"。用KMP算法首先得要让S2字符串创建一个匹配表,也就是看S2的前缀与后缀。先打个比方,什么是前缀和后缀
如图:
现在我们再对S2字符串进行分析,“部分匹配值”就是”前缀”和”后缀”的最长的共有元素的长度。以”ABCDABD”为例,
-”A”的前缀和后缀都为空集,共有元素的长度为0;
-”AB”的前缀为[A],后缀为[B],共有元素的长度为0;
-”ABC”的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
-”ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
-”ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为”A”,长度为1;
-”ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2;
-”ABCDABD”的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
所以S2字符串的匹配表为[0,0,0,0,1,2,0]
代码如下:
int [] next=new int[str2.length()];
public void createMatch(String str){
next[0]=0;
for (int i = 1, j=0; i < str.length(); i++) {
//KMP核心
while (j>0&&str.charAt(i)!=str.charAt(j)){
j=next[j-1];
}
if (str.charAt(i)==str.charAt(j)){
j++;
}
next[i]=j;
}
}
再这里重点说一下代码中的while循环,这个是重点,是核心。这个和下面搜索字符串的代码一起讲。
在创建完匹配表后,就可以进行对S1,S2匹配了,代码如下
/**
* KMP查找子串
* @param s1 父字符串
* @param s2 子字符串
* @param next 子字符串的部分匹配表
* @return
*/
public int select(String s1,String s2,int [] next){
for (int i = 0,j=0; i < s1.length(); i++) {
//KMP算法的核心
//自我认为 移动位数 = 已匹配的字符数 - 对应的部分匹配值 应该写这句话
//但是老师应该已经把这句话给转换了
//比如说BBC ABCDAB ABCDABDDBADC 这是s1
//ABCDABD [0,0,0,0,1,2,0]
//当i=10 发现S1字符串ABCDAB的后面是空格,S2字符串后面是D
//所以发现两者不一样,老师的第一步是将j变成2,也就是j=next[j-1],在匹配表对应2.
//再第二步 比较S2字符串ABC 与S1是否一致,不一致就继续找,找不到就退出,继续下一个for循环
while (j>0&&s1.charAt(i)!=s2.charAt(j)){
j=next[j-1];
}
if (s1.charAt(i)==s2.charAt(j)){
j++;
}
//
if (j==s2.length()){
return i-j+1;
}
}
return -1;
}
}
现在来说一下while循环
在我之前看到的word上是这么说的:
这个图片中的移动位数指的是将下面那个S2字符串向右移动4个位置,
然后在代码里的体现则是让 j 根据匹配表转移到S2的一个位置。像这里就是让j=next[j-1],因为这个匹配表创建的时候就是按照前缀后缀是否一样来进行匹配的,现在在S1中已经匹配到了AB,而在S2中AB后面又是C,但是后缀是ABD,因为S2中的ABD与S1中的AB ,(里面是空格)不同,当我们遇到S1与S2不匹配的时候,我们暴力匹配法就会从S2的头索引开始,又一次进行匹配。
第一步:但是KMP算法会根据匹配表,在这个情况下,根据匹配表,得到D前面的匹配表next[]数组,也就是next[j-1],发现值是2,那我们就可以根据KMP算法核心,让S2直接从第2个索引位置(也就是C)开始匹配。
第二步:让C匹配后,发现不相同,那继续找C的next[]数组的前一个值,发现数组的值为0,也就是next[]=[0,0,0,0,1,2,0],C所对应的数组下标为0,那就是直接退出,从头开始匹配。
然后继续for循环,查找S1中的字符与S2的首字符是否相同,继续按上面操作来进行。
总结:这个KMP算法真的是太奇幻了,这种算法根本想不到,都是前辈们钻研出来的,我们这个水平首先是要将这个算法会用,等以后才能真正懂得底层原理。KMP算法的匹配表真的是精妙了!!!如果有什么不理解的,真的需要自己去Debug调试调试,自己调试过后才会知道KMP算法的精妙之处,妙啊。