最简单的字符串算法,用于寻找字符串是否在文本中出现过
首先了解前缀和与后缀和:
字符串"ababc"的前缀包括:ε(空串)、"a"、"ab"、"aba"、"abab"和"ababc"
字符串"ababc"的后缀包括:ε(空串)、"c"、"bc"、"abc"、"babc"和"ababc"
在kmp中,前后缀应该去除本身和空串
以为例abcab
如果前后缀相同那么就意味着
a b c a b
a b c a b
这样在匹配时可以直接向后移,从而减少朴素算法的时间复杂度
一个较为简单的next函数
void get_next(string s,int *a)
{
for(int i = 1;i <= s.size();++i)
{
string str = s.substr(0,i);//字串
for(int j = 1;j < str.size();++j)
{
string x = str.substr(0,j);//前缀和
string y = str.substr(str.size()-j);//后缀和
if(x == y) a[i-1] = x.size();
}
}
}
效率较高的next函数
void get_next(string s, int *next)
{
int i = 1,j = 0;
next[0] = 0;
while(i < s.size())
{
if(j == 0 || s[i] == s[j]) next[++i] = ++j;
else j = next[j];
}
}
KMP算法实现如下
#include <bits/stdc++.h>
using namespace std;
void get_next(string s, int *next)
{
int i = 1,j = 0;
next[0] = 0;
while(i < s.size())
{
if(j == 0 || s[i] == s[j]) next[++i] = ++j;
else j = next[j];
}
}
int KMP(string s1,string s2,int *next)
{
int i = 0,j = 0;
for(;i < s1.size() && j < s2.size();++j,++i)
{
if(s1[i] != s2[j])
{
if(j != 0) j = next[j-1];
else j = 0;
}
}
if(j == s2.size()) return i-j;
else return -1;
}
int main()
{
string s1 = "aabaabaaf";
string s2 = "aabaaf";
int next[s2.size()];
fill(next,next+s2.size(),0);
get_next(s2,next);
cout << KMP(s1,s2,next);
return 0;
}