目录
算法目的
确定主串中所含子串(模式串)第一次出现的位置(定位)
算法应用
搜索引擎,拼写检查,语言翻译,数据压缩等。
算法种类
- BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的)
- KMP算法(特点:速度快)
BF算法
算法的思路是从S的每一个字符开始依次与T的字符进行匹配,采用穷举法的思路。
如图, 为主串的指向 ,为子串的指向,当匹配失败是, 和需要进行回溯,规则如下:
即 指向主串的下一个字符, 指向子串的第一个字符,一直到匹配成功或主串搜索完毕。
代码实现
需要说明的是,上面分析的是基于第一个元素的位置是1而言的,而我们实际编程的时候第一个字符的下标是0,索引回溯时有一点不同,但是主要思路还是不变的。
#include <iostream>
#include <string>
using namespace std;
int bfsearch(string s, string t)
{
int i,j = 0;
while(i < s.size() && j < t.size())
{
if(s[i] == t[j])
{
i++;
j++;
}
else
{
i = i - j + 1; // 这里并不是 i = i - j + 2, 原因是实际编程时起始下标为 0
j = 0;
}
}
if(j >= t.size())
{
return i - j;
}
else
{
return -1;
}
}
void test01()
{
string s = "abcdefg";
string t = "def";
cout << "主串 s = " << s << endl;
cout << "子串 t = " << t << endl;
cout << "子串 t 在主串 s 的 " << bfsearch(s, t) << " 号位。(索引从 0 开始。)" << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果
对于主串 s 而言,第一个元素 a 的下标为 0,即 子串 def 在其的 3 号位,运行结果一致~
KMP算法
KMP算法是D.E.Kunth、J.H.Morris 和 V.R.Pratt共同提出的,简称KMP算法。该算法较BF算法有较大改进,从而使算法效率有了某种程度的提高。利用已经部分匹配的结果而加快模式串滑动的速度,且主串S的指针i不避回溯!这样可将时间复杂度提升至O(n+m)!
主要思想就是若模式串中存在相等的前后缀,那么上一次匹配的后缀部分等同于下一次匹配的前缀部分,此时直接从相等的前缀部分的下一个字符开始匹配!
next数组
- 前缀:包含首位字符但不包含末尾字符的子串
- 后缀:包含末位字符但不包含首位字符的子串
- next数组定义:当主串与模式串的某一位不匹配时,模式串要退回的位置
- next[j]: 其值 = 第 j 位字符前面 j - 1 位字符串组成的子串的前后缀重合的字符数 +1
- 复杂度:构造next数组算法的时间复杂度和空间复杂度均为.
代码实现
#include <iostream>
#include <string>
using namespace std;
// 获取next数组
void getNext(int* next, const string& s)
{
int j = 0;
next[0] = 0;
for(int i = 1; i < s.size(); i++)
{
while (j > 0 && s[i] != s[j])
{
j = next[j - 1];
}
if (s[i] == s[j])
{
j++;
}
next[i] = j;
}
}
int kmpsearch(string s, string t)
{
if (t.size() == 0)
{
return 0;
}
int* next = new int[t.size()];
getNext(next, t);
int j = 0;
int index = -1;
for (int i = 0; i < s.size(); i++)
{
while(j > 0 && s[i] != t[j])
{
j = next[j - 1];
}
if (s[i] == t[j])
{
j++;
}
if (j == t.size())
{
index = i - t.size() + 1;
}
}
delete[] next;
next = nullptr;
return index;
}
void test01()
{
string s = "abcdefg";
string t = "def";
cout << "主串 s = " << s << endl;
cout << "子串 t = " << t << endl;
cout << "子串 t 在主串 s 中的位置是:" << kmpsearch(s, t) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果
与预想结果一致,但是现在好像还没有考虑到在主串中重复存在模式串的情况,或者是默认只匹配一次!所以算法仍需改进!