稍微看了下别人的题解,反正我是从来都记不住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);
    }
}