1--原理
视频讲解参考:最浅显易懂的 KMP 算法讲解
原理讲解参考:KMP算法详解
2--代码
以下代码实现基于如下next数组规定:
str: abcbab → next: 0 0 0 0 1 2 (即当前字符 next 数组的后缀包括当前字符本身,且规定第0个字符的前缀为0,从第1个字符开始构建next数组);
C++:
#include <vector>
#include <string>
#include <iostream>
class KMP{
public:
int kmp_search(std::string String, std::string Patt){
build_next(Patt); // 构建next数组
int i = 0;
int j = 0;
while(i < String.length()){
if(String[i] == Patt[j]){
i += 1;
j += 1;
}
else if(j > 0){
j = next[j - 1];
}
else{
i += 1;
}
if (j == Patt.length()){
return i - j;
}
}
return -1;
}
void build_next(std::string Patt){
next.push_back(0); // 第0个字符的next数组为0
int j = 0; // 从第0个字符开始 j指向前缀最末尾的元素(索引即长度)
int i = 1; // 从第一个字符开始遍历 i指向后缀最末尾的元素
while (i < Patt.length()){
if (Patt[j] == Patt[i]){
j += 1;
i += 1;
next.push_back(j);
}
else{
if (j == 0){
next.push_back(0);
i += 1;
}
else{
j = next[j - 1];
}
}
}
}
private:
std::vector<int> next; // next数组
};
int main(int argc, char* argv[]){
std::string String = "ababcbc";
std::string Patt = "abcb";
KMP Solution;
int pos = Solution.kmp_search(String, Patt);
std::cout << "Position: " << pos << std::endl;
return 0;
}
python:
# Test KMP Algorithm
def build_next(patt): # 对子串构建其next数组
next = [0] # 第0个字符的next数组为0
j = 0 # 从第0个字符开始
i = 1 # 从第1个字符开始遍历
while i < len(patt):
if patt[j] == patt[i]: # 当前字符i与字符j相同
j += 1 # 右移
i += 1 # 右移
next.append(j) # 用j表示当前字符i的相同前后缀长度
else: # 当前字符i与字符j不相同
if j == 0: # 如果字符j已经是第0个字符
next.append(0) # 则当前字符i的相同前后缀长度为0
i += 1 # 比较下一个字符
else: # 如果字符j不是第0个字符
j = next[j - 1] # 比较字符j前一个字符j-1的next数组对应位置的字符
return next
def kmp_search(string, patt): # string主串 patt待匹配的子串
next = build_next(patt)
i = 0
j = 0
while i < len(string):
if string[i] == patt[j]: # 字符匹配,右移下一位
i += 1
j += 1
elif j > 0: # 字符不匹配, j回退并跳过字符
j = next[j - 1] # 跳过的字符取决于前一个字符对应的next数组
else: # 字符不匹配, j已经处在子串第一个字符中
i += 1 # 从主串下一个字符开始匹配
if j == len(patt): # 表明子串的所有字符都匹配成功
return i - j # 返回匹配主串的第一个字符位置
if __name__ == "__main__":
string = "ababcbc"
patt = "abcb"
index = kmp_search(string, patt)
print("start_index: ", index)