2021-05-12 更新
- 两个步骤
- ne数组求解
- 匹配
- 整体思想
两个步骤,都是i按照循环从1走到最后,j不断进行调整。匹配就两者都往后,不匹配j就往前跳,跳到头就结束。
ne 数组举例模拟
const int N = 5e4 + 10;
int ne[N];
class Solution {
public:
int strStr(string s, string p) {
int m = s.size(), n = p.size();
if(n == 0) return 0;
// 下标从1开始,故最前面插入0
s.insert(0, " ");
p.insert(0, " ");
// 为什么非要用 j + 1 ?,ne[i] = j 中,j>0,恰好就能和s[i]的后缀匹配上,且j就是在p中,这个局部匹配串里最后那个元素的位置。 j + 1表示它的下一个。这么做方便理解和编程
for(int i = 2, j = 0; i <= n; i ++){
while(j && p[i] != p[j + 1]) j = ne[j]; // j > 0的条件就是当j 退回到1这个位置,说明从头匹配,直接退出。不加条件会死循环
if(p[i] == p[j + 1]) j ++;
ne[i] = j;
}
for(int i = 1, j = 0; i <= m; i ++){
while(j && s[i] != p[j + 1]) j = ne[j];
if(s[i] == p[j + 1]) j ++;
if(j == n){
return i - n; // 如果从1开始 i - n + 1
// j = ne[j] 继续匹配
}
}
return -1;
}
};
class Solution {
public int strStr(String haystack, String needle) {
if(needle.equals("")) {
return 0;
}
if(haystack.equals("")) {
return -1;
}
// 构造KMP中的dp矩阵
int m = needle.length();
// 各个状态(行)遇到下一个字符(列)跳转到哪个状态
int[][] dp = new int[m][256];
// 影子状态
int X = 0;
dp[0][needle.charAt(0)] = 1;
for (int i = 1; i < m; i++) {
for (int j = 0; j < 256; j++) {
//假设下个字符不匹配,此时要回去看影子状态,从而得知跳转到哪个状态
dp[i][j] = dp[X][j];
}
// 只有pat上i的字符匹配,跳转到下个状态
dp[i][needle.charAt(i)] = i + 1;
// 更新影子状态
X = dp[X][needle.charAt(i)];
}
// 构造dp完成后,开始search
// 初始状态为0
int s = 0;
for (int i = 0; i < haystack.length(); i++) {
s = dp[s][haystack.charAt(i)];
if (s == m) {
return i - m + 1;
}
}
// 匹配失败,返回-1
return -1;
}
}