匹配串(i下标):BBCABCABCABD
模式串(j下标): ABCABD
求解部分匹配值表:模式串(ABCABD)
求解部分匹配值表:模式串(ABCABD)
| 模式串 | A | B | C | A | B | D |
|---|---|---|---|---|---|---|
| i | 0 | 1 | 2 | 3 | 4 | 5 |
| j | 0 | 0 | 0 | 1 | 2 | 0 |
| next[i]=j | 0 | 0 | 0 | 1 | 2 | 0 |
package xcrj.string;
/**
* 模式匹配算法
*/
public class KMP {
/**
* 部分匹配表
* 本质就是求已匹配的子串的最长部分匹配前缀的字符数
*
* 当无法全部匹配时,进行部分匹配j起始位置
* 模式串的子串的最长前缀=最长后缀 的字符数,就是当前模式串子串的部分匹配
* 部分匹配值就是模式串的子串的子串的字符数
*
* @param pattern 模式串
* @return int[]next 部分匹配表
*/
private static int[] partialMatchingTable(String pattern) {
int[] next = new int[pattern.length()];
// i=0,1个字符做1个子串时,部分匹配值=0
next[0] = 0;
// 求得0~i-1子串的部分匹配值基础上,再求0~i子串的部分匹配值
for (int i = 1, j = 0; i < pattern.length(); i++) {
// i,j字符不匹配时,j退回到0~j-1子串的部分匹配值
while (j > 0 && pattern.charAt(j) != pattern.charAt(i)) {
// j退回到0~j-1子串的部分匹配值(0~j-1的子串都是部分匹配的)
j = next[j - 1];
}
// i,j字符匹配,比较下一个字符
if (pattern.charAt(i) == pattern.charAt(j)) {
j++;
}
// 0~i的子串的部分匹配值=j
next[i] = j;
}
return next;
}
/**
* kmp算法
*
* @param match 匹配串
* @param pattern 模式串
* @return -1表示匹配串中不存在模式串,非-1表示模式串在匹配串的起始位置
*/
public static int kmp(String match, String pattern) {
// 获取部分匹配表
int[] next = partialMatchingTable(pattern);
// i匹配串下标,j模式串下标
for (int i = 0, j = 0; i < match.length(); i++) {
// 不完全匹配时,进行部分匹配:i,j字符不匹配时,从模式串最长部分匹配前缀之后开始比较
while (j > 0 && match.charAt(i) != pattern.charAt(j)) {
j = next[j - 1];
}
// i,j字符匹配,比较下一个字符
if (match.charAt(i) == pattern.charAt(j)) {
j++;
}
// 模式串匹配完成,返回模式串在匹配串的起始位置
if (j == pattern.length()) {
return i - j + 1;
}
}
return -1;
}
public static void main(String[] args) {
String match = "CBCBA";
String pattern = "CBA";
int idx = kmp(match, pattern);
System.out.println("模式串在匹配串的起始位置:" + idx);
}
}