稍微看了下别人的题解,反正我是从来都记不住next数组是怎么计算的。。。
此解法来自labuladong大佬最基础的kmp:利用状态机的思想,当字符匹配的时候推进状态;当字符不匹配的时候要回到某个状态(有相同前缀的状态)
二维数组虽然增加了空间复杂度,但是好理解多了好嘛
class KMP {
private String pattern;
private int[][] dp;
private int n;
public KMP(String pattern) {
this.pattern = pattern;
this.n = pattern.length();
this.dp = new int[n + 1][256]; // 因为匹配完模式串之后,还要继续往后匹配,所以这里第一维的长度是n+1
init();
}
// 如果是找到子串出现的次数,则在匹配完之后,回到最后状态的影子状态
private void init() {
int X = 0; // 影子状态:即模式串要“重启”到的位置,影子状态总是落后最新状态(search时的j)一个状态,并和j拥有最长的相同前缀
dp[0][pattern.charAt(0)] = 1; // 只有第一个字符匹配时才能向前推进状态,遇到其他字符不推进
for (int i = 1; i < n; i++) { // 0已经处理过了,这里就从1开始
for (int c = 0; c < 256; c++) {
if (c == pattern.charAt(i)) dp[i][c] = i + 1; // 如果匹配,则状态向前推进1位
else dp[i][c] = dp[X][c]; // 利用影子状态重启
}
X = dp[X][pattern.charAt(i)]; // 推进影子状态
}
dp[n][pattern.charAt(n - 1)] = X; // 因为这里是计算出现次数,所以在匹配完之后,要额外记录一下匹配完之后应该回到的影子状态
}
public int search(String str) {
int ln = str.length();
int j = 0; // 模式串的初始状态
int r = 0; // 记录出现的次数
for (int i = 0; i < ln; i++) {
j = dp[j][str.charAt(i)]; // 推进模式串的状态
if (j == n) { // 匹配完了就计数器+1并回到最近的影子状态
r++;
j = dp[j][str.charAt(i)];
}
}
return r;
}
}
public class Solution {
public static void main(String[] args) {
String pattern = "aabaa";
String str = "aabaabaa";
System.out.println(new Solution().kmp(pattern, str));
}
public int kmp(String S, String T) {
KMP kmp = new KMP(S);
return kmp.search(T);
}
} 
京公网安备 11010502036488号