在字符串的模式匹配中,KMP算法算的上经典。
在KMP算法中,next数组的计算是关键。
以下是几种求解next数组的方法:
1)个人认为最好理解的方法
void getNext(string str, vector<int> &next){
if(str.empty()){
next.clear();
return;
}
int len = str.length();
next.resize(len);
next[0] = -1;
for(int i = 1; i < len; ++i){
int j = next[i-1];
while(j!=-1 && str[j] != str[i-1])
j = next[j];
next[i] = j+1;
}
}
2)统一在一个循环里面
void getNext(string str, vector<int> &next){
if(str.empty()){
next.clear();
return;
}
int len = str.length();
next.resize(len);
next[0] = -1;
int i = 0, j=-1;
while(i+1 < len)
{
if(j==-1||str[i]==str[j])
{
++i;++j;
next[i]=j;
}
else
j=next[j];
}
}
上面两种实现,都是next数组的朴素实现。但是,在串中有重复的子串时,所求的的next数组并不是好的,
比如:
字符串 a b c d a b c d
next值-1 0 0 0 0 1 2 3
实际上,下面的next数组更优:
next值-1 0 0 0 -1 0 0 0
为了解决有重叠子串时,next数组不合理的问题,通常做一些改进:
实现1)的改进版本,注意j定义的位置和意义的变化
void getNext(string str, vector<int> &next){
if(str.empty()){
next.clear();
return;
}
int len = str.length();
next.resize(len);
next[0] = -1;
int j = -1;
for(int i = 1; i < len; ++i){
while(j!=-1 && str[j] != str[i-1])
j = next[j];
++j;
//next[i] = j+1;
if(str[i] == str[j])
next[i] = next[j];
else
next[i] = j;
}
}
实现2)的改进版本,注意j定义的位置和意义的变化
void getNext(string str, vector<int> &next){
if(str.empty()){
next.clear();
return;
}
int len = str.length();
next.resize(len);
next[0] = -1;
int i = 0, j=-1;
while(i+1 < len)
{
if(j==-1||str[i]==str[j])
{
++i;++j;
// next[i]=j;//普通
//=======优化========
if(str[i]!=str[j])
next[i]=j;
else
next[i]=next[j];
}
else
j=next[j];
}
}
下面的代码是KMP算法中比较函数的实现:
int kmp(string s, string p) {
if(s.empty() && p.empty())
return 0;
vector<int> next;
getNext(p, next);
int i = 0, j = 0;
while(i < s.size() && (j == -1 || j < p.size()) ){
if(j == -1 || s[i] == p[j]){
++i;
++j;
}
else
j = next[j];
}
if(j == p.size())
return i - j;
return -1;
}