import java.util.Arrays;
public class KMPTest {
public static void main(String[] args) {
//源串
String x = "BBC ABCDAB ABCDABCDABDE";
//目标串
String z = "ABCDABD";
// int []table = getTable(z);
// System.out.println(Arrays.toString(table));
int s = KMP(x,z);
System.out.println(s);
}
//kmp搜索
public static int KMP(String a,String b){
int []c = getTable(b);//获取目标串的部分匹配表
//i代表源串的当前索引 j代表目标船的当前索引
for (int i=0,j=0;i<a.length();i++){
//如果当前目标串 与 源串前面已经有相同的字符了(当前源串对比的 是目标串第一个字符后面的字符时 不能是第一个字符)
//并且当前源串索引对应值 不等于当前目标串索引对应值时 设置 j = c[j-1](部分匹配表[当前索引的前一个的(因为当前值不同 前一个值相同)部分匹配值])
//如果相同那么j++ (i在for中本来就是每轮+1)对比下一位 如果对比的目标串的索引已经超出了目标串 那么代表已经找到目标了
//如果值不同 且j=0 那么什么都不触发 进下次for循环i++ 在比
while (j>0&&a.charAt(i)!=b.charAt(j)){
j = c[j-1];
}
if (a.charAt(i) == b.charAt(j)){
j++;
}
//如果只需要找一个目标那么这样返回 如果需要找多个目标 那么当此条件达成时重置j = j-1的部分匹配值(因为此时j已经超出了)
if (j == b.length()){
return i-j+1;
}
}
return -1;
}
//获取部分匹配表
public static int[] getTable(String x){
//创建部分匹配表
int []s = new int[x.length()];
//因为当串的长度为1时 根本没有前后缀 所以他的部分匹配值必定未定
s[0] = 0;
//i代表当前串的最后一个元素 也就是后缀的最后一个
// j代表当前 后缀的最后一个也就是i 应该和前缀的第几位比
for (int i=1,j=0;i<x.length();i++){
while (j>0 && x.charAt(i) != x.charAt(j)){
j = s[j-1];
}
//对比当前 i所对应的元素和j所对应的元素 如果相同 那么j++ 并设置部分匹配表当前字符长度
// (比如完整串为: ABCDABD时 现在i为4 串为:ABCDA(索引从0开始 i代表后缀的索引)
// 而i代表的是后缀j代表的是前缀 这时 i("A"BCDA)和j(ABCD"A")比较 发现他们相同 于是j++
// 再设置部分匹配表中i位置(也就是从头到这个字符 这整个字符的部分匹配值为1)的值为j(1)
// 因为前面也在这样对比但是前面 i与j对应索引的值从未相同 所以j为1 按现在的情况进入下次for循环
// 此时i=5 j=1 也就是A "B(j)" CDA "B(i)" 中 这两个值比较 这是因为当前i和j的前一个元素已经相同了
// 这时我们发现当前ij也是相同的 那么j再次++ 设置当前i(也就是上一个i的后面)索引的部分匹配表的值为j(2)
// 此时再向后比 ABCDABD i=6 j=2 也就是AB "C(j)" DAB"D(i)" 发现他们不相同并且此时j>0(意思就是在尝试延长上个匹配串的长度)
// 于是进入while循环 并设置 j = 0(部分匹配表[j-1](这个[]中的值代表当前索引2-1的部分匹配值)) j=0退出while循环
// 再次判断ij对应的值 i=6 j=0)
// 时的字符匹配值
//j++以为
if (x.charAt(i) == x.charAt(j)){
j++;
}
s[i] = j;
}
return s;
}
}
09-19
1913
09-16
1384
09-26
09-26
09-26