数据结构讲到字符串匹配的时候,讲到了一个很优秀的算法——KMP算法!
相对于传统的匹配算法,KMP算法有自己的一定优势。当字符串中有很长的一段重复字符的时候,可以省略过很大的一部分无效比较。跟传统的匹配算法不同的是,KMP算法引进了一个新的东西——
跳转表,就是这个表在匹配时发挥了巨大的作用。
这是课本上的原理与例子,我加上了一些自己的理解。所以最重要的是得出跳转表next[]来辅助我们匹配字符串。首先是第一种匹配表:
void get_nextval1(string a,int next[])
{
next[0] = -1;
int i = 0;//前缀,不回溯
int j = -1;//模式串回溯
while (i < a.length() - 1)
{
if (j == -1 || a[i] == a[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
}
这种版本的跳转表还有一个问题就是前面有很多的字符相同的时候,仍得一个一个匹配,所以经过改进有了第二种改进版的跳转表:
void get_nextval2(string a, int next[])
{
next[0] = -1;
int i = 0;//前缀,不回溯
int j = -1;//模式串回溯
while (i < a.length() - 1)
{
if (j == -1 || a[i] == a[j])
{
++i;
++j;
if (a[i] == a[j])
next[i] = next[j];
else
next[i] = j;
}
else
j = next[j];
}
}
这种跳转表就很好的回避了那个问题,当相同的时候会滑过那一串,具体的例子如下图:
(嗯,图片斜着是为了治大家的颈椎病的。。。)
其实,KMP算法的思路很好理解,但是在实现的时候如果把数据结构书上的代码写进去会发现各种越界,这就是另一个问题了,在数据结构中,第一个位置被默认存放了字符串的长度,所以在写为代码的时候,应该使用length函数得到长度,而且所有的位置都应比书上少1!
最后上主函数和KMP函数。。。
int main()
{
int a[100];
string partern;//模式字符串
string search;//匹配字符串
cout << "请输入模式字符串:";
cin >> partern;
cout << "请输入匹配字符串:(匹配字符串应长于模式字符串!)";
cin >> search;
get_nextval2(partern,a);
if (kmp(search, partern, a))
cout << "匹配成功!" << endl;
else
cout << "匹配失败!" << endl;
return 0;
}
int kmp(string a, string b, int next[])//a为匹配字符串,b为模式字符串
{
int i = 0;
int j = 0;
while (i < a.length())
{
if (j == -1 || a[i] == b[j])
{
++i;
++j;
}
else
j = next[j];
}
if (j == b.length())
return i - b.length();
else
return 0;
}