传统的字符串比较需要不停的回溯,重复比较,但是已经比较并匹配的字符其实是已知的就是待匹配字符串自身的字符,如此可以根据这些已知信息减少回溯的字符。整个字符串的匹配只需要不停匹配下一个字符。那么待匹配字符串每次回溯的位置则根据自身决定(根据前面已经匹配的字符串)并不需要每次回溯的开头的位置。既然是根据自身,就可以先计算出每个字符的回溯位置,即next数组。next数组即表示从第二个字符开始到当前字符位置的子字符串和其自身(从第一个字符开始的子字符串)的匹配的字符个数。比如abcab 的next数组(构建next数组的复杂度是o(m) m是待匹配字符串的长度)
a | b | c | a | b |
0 | 0(a,b不匹配) | 0(a,c不匹配) | 1(a,a匹配) | 2(ab,ab匹配) |
class StringPattern {
public:
int kmp_search(string& str, string& find_str, vector<int>& next){
int n = str.size();
int j = 0;
int i = 0;
while (i<n){
if (str[i] == find_str[j]){
++i;
++j;
if (j == find_str.size())return i - find_str.size();
}
else{
if (j > 0)
j = next[j - 1];
else
i++;
}
}
return -1;
}
void nextstep(string str, vector<int>& next){
int n = str.size();
if (n <= 0)return;
int i = 0;
int j = 1;
next[0] = 0;
while (j < n){
if (str[j] == str[i]){
i++;
next[j] = i;
}
else{
next[j] = 0;
i = 0;
}
j++;
}
}
int findAppearance(string A, int lena, string B, int lenb) {
if (lena <= 0 || lenb <= 0 || lena != A.size() || lenb != B.size())return -1;
if (lenb>lena)return -1;
vector<int> next(lenb,0);
nextstep(B, next);
return kmp_search(A, B, next);
}
};
int main(){
StringPattern s;
string str = "bc";
string str1 = "acbc";
cout << s.findAppearance(str1,4,str,2);
return 0;
}