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] 算法学习笔记