简而言之就是给你两个字符串,一个主串,一个子串,我们写一个算法,看是否能在主串中找到子串,并且返回相匹配的子串第一个字符在主串中出现的位置!
BF
思路:
这个算法是一个比较暴力的算法
假设:
主串的长度 = n;
字串的长度 = m;
如图所示,i初始值为pos,j的初始值为0,分别拿 i对应的值与j对应的值相比较,如果相等,则i++,j++;直到j的值大于等于字串的长度,这个时候说明主串中有字串,此时返回i-j即可,如果j达到字串的长度之前,两个值就不相等,那么i = i-j+1,j = 0;拿之前i的第一个位置的下一个位置和字串重新比较。
假设第一次是从主串的第一个字符a开始比较,那么第二次就是拿b开始的主串与子串比较。一直比,直到j>=子串的长度或者i>=主串的长度。
时间复杂度
假设从主串的第i个位置开始与子串匹配成功,则在前i-1躺匹配中字符总共比较了(i-1)m次;若第i躺成功的字符比较次数为m,则总的比较次数时im,假设是从最后一个字符才匹配成功的,则i = n-m+1;
所以时间复杂度是(n-m+1)m,那么时间复杂度为O(mn);
代码如下:
#include<string>
#include<iostream>
using namespace std;
string s = "ababcabcacbab";
string a = "abcac";
int BF(string s1, string s2,int pos)//pos表示在主串的第pos位置开始查找
{
int i = pos ,j = 0;//i表示主串的下标,j表示字串的下标
if (s.size() == 0 && s2.size() == 0) return -1;
if (pos < 0 || pos >= s1.size()) return -1;//养成好的代码习惯hh
while (i < s1.size() && j < s2.size())
{
if (s1[i] == s2[j])
{
i++;
j++;
}
else
{
i = i - j + 1;//i-j表示主串中最开始和字串比较的下标 +1表示要用主串的下一个位置再重新和子串比较
j = 0;
}
}
if (j >= s2.size())
return i - j;
return -1;
}
int main()
{
printf("%d\n", BF(s, a,0));//打印a子串在s串中的位置
return 0;
}
KMP
时间复杂度 O(m*n)
思路
上述的BF算法中,如果子串与主串出现不匹配的情况,那么i需要改编成i-j+1,但是KMP算法不用改变i的值,主串的i只需要在与字串字符相等的时候i++,不需要回溯(就是i = i-j+1)并且上述BF算法中,当出现子串与主串不匹配时,子串的j会从头开始,这在KMP算法中也是改变的点。下面我用图讲解一下
首先:为什么主串可以不退?即i值不会减。
为什么可以这样?
引出next数组 next[j] = k,k表示的是当子串位于j时不匹配的时候j的后退值,即如果不匹配的时候j的重新取值。
next的目测法:
上面的是目测法,转化成代码的话我们还得分析一下:
#include<iostream>
#include<assert.h>
#include<string>
using namespace std;
string s, p;//s表示主串p表示子串
void Get_next(int* next, string p)
{
//根据之前提到的,无论何种情况,next[0] = -1 next[1] = 0
next[0] = -1;
next[1] = 0;
//next的下标从2开始
int j = 2;
int k = 0;//注意:此时相当于只知道next[j-1]的值,需要求的是next[j]的值
//所以我们需要比较的是next[j-1]与其对应的k
while (j < p.size())
{
if (k==-1||p[j - 1] == p[k])//因为一开始我们需要求的只是next[2]而恰好next[1]与j = 1对应的k值我们都知道
//所以这样写
//当第一个值都不符合的时候,k = -1.所以也应该满足条件
{
next[j] = k + 1;//满足p[j-1]==p[k]的时候
k++;
j++;
}
else
{
k = next[k];
}
}
}
int KMP(string s, string p, int pos)//表示主串从pos开始匹配
{
if (s.size() == 0) return -1;
if (pos >= s.size()) return -1;
int* next = (int*)malloc(sizeof(int) * p.size());
assert(next);//判断next不为NULL
Get_next(next, p);//得到子串的next值
int i = pos, j = 0;
while (i < s.size() && j < p.size())
{
if (j == -1 || s[i] == p[j])//当next[j]使得j为-1的时候其实就是拿子串的第一个元素与主串比较
{
i++;
j++;
}
else
j = next[j];
}
if (j >= p.size())//跳出循环的有两种情况,当满足此条件时说明匹配成功
return i - j;
return -1;
}
//ababcabcacbab
//abcac
int main()
{
cin >> s >> p;
printf("%d\n", KMP(s, p, 0));
return 0;
}
KMP实在是太抽象了,如果解释的不是很清楚还请见谅~~
求赞,求关注,求三连!!!