KMP字符串匹配
Knuth-Morris-Pratt
KMP算法是一种在文本串s中快速查找模式串p的一种算法。
动态规划实现
关键步骤:构建状态转移数组
package code;
/**
* 动态规划实现KMP
*/
public class KMP {
private int[][] dp;
public int getIndex(String s, String pattern) {
buildFSM(pattern);
return search(s, pattern);
}
public void buildFSM(String pattern) {
int m = pattern.length();
/**
* 状态转移数组
* @param m 一共有 m 个状态
* @param 26 本次只考虑小写字母字符串的匹配情况,可自由扩展
*/
dp = new int[m][26];
/**
* 初始状态定义
* @param prefixState 表示当前位置的前缀状态,匹配字符失败时要回退的状态
*/
dp[0][pattern.charAt(0) - 'a'] = 1;
int prefixState = 0;
/**
* 状态转移
*/
for (int i = 1; i < m; i++) {
for (int j = 0; j < 26; j++) {
dp[i][j] = dp[prefixState][j]; // 全部使用前缀状态做初始填充
}
/**
* 更新下一状态和前缀状态
*/
dp[i][pattern.charAt(i) - 'a'] = i + 1;
prefixState = dp[prefixState][pattern.charAt(i) - 'a'];
}
}
public int search(String s, String pattern) {
int m = pattern.length();
int n = s.length();
int state = 0;
for (int i = 0; i < n; i++) {
state = dp[state][s.charAt(i) - 'a']; // 计算下一状态
// 到达最终状态, 匹配成功
if (state == m) {
return i - m + 1;
}
}
return -1;
}
}
传统实现
关键步骤:构建
next
数组
什么是 next
数组?
next
数组中保存着当前位置真前缀与真后缀的交集中最长元素的长度(真前缀的意思是不能是整个串,必须是子串)。
Example
模式串(p):"ababcabaa"
对应的 next
数组:
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
p | a | ab | aba | abab | ababc | ababca | ababcab | ababcaba | ababcabaa |
next | 0 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 1 |
package code;
/**
* 传统KMP实现
*/
public class KMP {
private int[] next;
/**
* 计算最长公共前后缀长度, 构建 next 数组
* @param pattern
*/
public void buildNext(String pattern) {
int m = pattern.length();
next = new int[m];
next[0] = 0; // 单个字符只能为0
/**
* 将 pattern 右移1位, 自己匹配自己(用前缀匹配后缀)
* @param i 指示后缀
* @param j 指示前缀
*/
for (int i = 1, j = 0; i < m; i++) {
while (j != 0 && (pattern.charAt(i) != pattern.charAt(j))) {
j = next[j - 1];
}
if (pattern.charAt(i) == pattern.charAt(j)) {
j += 1;
}
next[i] = j;
}
}
public int search(String s, String pattern) {
for (int i = 0, j = 0; i < s.length(); i++) {
/**
* 字符匹配失败, 则回退到上一状态
* i指针不变; j指针左移
*/
while (j != 0 && s.charAt(i) != pattern.charAt(j)) {
j = next[j - 1];
}
if (s.charAt(i) == pattern.charAt(j)) {
j += 1;
}
if (j == pattern.length()) {
return i - j + 1;
}
}
return -1;
}
}
参考文章:
[1] KMP算法详解
[2] 算法学习笔记