如此愚鈍 一天才學會
KMP算法的思想就是,如果已經匹配的模式串內部有重複的部分,那麼我們移動的時候就能多移動幾步,而且由於模式串的大小都是固定的,所以我們可以提前的求出有哪些部分是重複的。
這裏說的重複是指,前綴子串和後綴子串
舉個例子: e x a m p l e
- 前綴子串: e , ex, exa, exam , examp , exampl
- 後綴子串: e , le , ple, mple , ample , xample
找到它們之間的公共部分記錄在next數組裏面,其實next數組是最難求的。
#include <iostream>
#include <string>
class KMP {
public:
KMP(const std::string &str) \
: str_(str) {
next_ = new int[str.size()];
}
/* | <- 壞字符
a b a b a e s d f ...
a b a b a c e b
next_[4] = 2
3
a b a b a c
*/
int find(const std::string &mod) const {
int j = 0;
init_next(mod);
for(int i = 0; i < str_.size() ; i++) {
while(j > 0 && str_[j] != mod[i]) { //如果不相等的話就向後移動
j = next_[j - 1]; //將模式串移動到前一位對應的位置上面
}
if(str_[j] == mod[i]) { //如果相等的話就向下走
j ++;
}
if(j == mod.size()) {
return i - mod.size() - 1; //找到合適的
}
}
return -1;
}
//前綴結尾字符下標 最長可匹配前綴子串結尾的字符下標。
void init_next(std::string &mod) const {
next_[0] = 0;
int j = 0;
for(int i = 1; i < mod.size() ; ) {
if(mod[i] == mod[j]) {
next_[i] == ++j;
++ i;
}else {
if(j == 0) //如果j已經退回到0 還是不相等
{
next_[i] = 0;
i ++;
}
else {
while(mod[i] != mod[j] ) {
j = next_[j - 1]; //j變爲壞字符前面存儲的值
}
}
}
}
}
private:
int *next_;
std::string &str_;
};