串的模式匹配(Pattern Matching)有一主串s1,一待匹配串t1。现在将s1的子串和t1匹配,若找到,则输出location;若没找到,则输出-1
然后,在模式匹配中(n是s1长度,m是t1长度)
(1)朴素算法就是笨方法,像我这种笨蛋想到的第一种解决方式就是它,逐一比较,复杂度是n*m;
(2)有一种神奇的算法叫KMP,使得匹配速度加快(特别是n远远大于m时),降低了时间复杂度和s1长度的相关性(瞎说的,直觉是这样),因为本算法会吸取教训,让子串的匹配不重头开始逐一比较,而是退回到一定位置。
给人感觉就是,我和你长得挺像的,都是两只胳膊两条腿,比到一半发现咱们脸长的很不同,这时候,没必要把你和我的比较重新放到生物和非生物的层次来进行,而是缩小范围,放到你和我是什么灵长类范围来比较。(瞎说的,目前理解比较浅,以后想到更好的说法再修改。)
关于KMP算法的细节,此处不详细说,可以自行百度。(其实就是我现在理解比较浅,没想到通俗易懂的骚话)
接下来是算法的实现。
我在理解课本基础上写的
// DS_KMP_original.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int kmp(string s1, string t1);
int main()
{
string s1 = "ababaababac", t1 = "ababac";
cout << "the location is " << kmp(s1, t1) << endl;
system("pause");
return 0;
}
int kmp(string s1, string t1)
{
int location = -1;
//(1).initialize the next[]
vector<int> next;
next.resize(t1.size());
next[0] = -1;
next[1] = 0;
for (int p = 2; p <= t1.size() - 1; ++p)//next[p]
{
for (int i = 0; i <= p - 2; ++i)//span
{
bool judge = true;//the signal
for (int j = 0, k = (p - 1) - i; j <= i; ++j, ++k)//specification
{
//once wrong, false
if (t1[j] != t1[k])
{
judge = false;
break;
}
//right till the end, true
}
if (judge)
next[p] = i + 1;
}
}
//(2).match the t1 in the s1 to find the location
int j = 0;
for (int i = 0; i <= s1.size() - 1; ++i)
{
//when the char is compatible
if (t1[j] == s1[i])
{
//the end
if (t1.size() - 1 == j)
{
location = i - (t1.size() - 1);
break;
}
//not the end
else
{
++j;
continue;
}
}
//when the char is not compatible
else
{
j = next[j];
if (-1 == j)
{
j = 0;
continue;
}
else
{
--i;// to keep i unchanged
continue;
}
}
}
return location;
}
时间复杂度是m的三次方+n(目前感觉是)
教科书上的设计思路
时间复杂度是m+n
综上所述,显然我写的比较费时。原因是在计算next[]时,也可以吸取教训,书本的:指针j在前面,指针k在后面,相同,可以赋值给++j; ++k; next[j] = k,不同,k=next[k],其实就是回溯;而我则没利用这一点。
2021.3.2补充:
关于教科书内容的理解;
1.有两个指针分别为,j和k,j在前线,k在后方;
2.当两个指针所指的字符相等,j和k分别++,并令next[j]=next[k];
3.(设主串的比较指针为i)实质上,这里可以看j指针为当前和i指针所指比较的指针,如果j所指和i所指不同,j回溯,那么回溯到哪里呢,回溯到k,为什么呢,因为我确保了[j-1]之前的串串和[k-1]之前的串串是相同的,这不正是kmp算法的精髓吗;
4.当两个指针所指的字符不同,k回溯,即k=next[k],当前的k待机,等待下次循环时比较 两个指针所指的字符,这样又回到了2和4的判断;