目录
案例:假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;
案例:假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;
1.BF算法(暴力算法)
思路:遍历主串,子串和当前位置的比较;匹配成功返回主串当前位置,主串遍历完成都没有匹配成功,返回-1;
代码:
#include<iostream>
#include<string>
using namespace std;
int BF(string str,string sub)
{
int strlen=str.size();
int sublen=sub.size();
//遍历主串,和子串比较
for(int i=0;i<strlen-sublen+1;i++)
{
int t=i,j=0;
for(;j<sublen;j++)
{
if(str[t++]!=sub[j])
break;
}
if(j==sublen)
return i;
}
return -1;
}
int main()
{
cout<<BF("ababcabcdabcde","abcd")<<endl;
return 0;
}
结果:
时间复杂度:O(N^2) (或者O(strlen*sublen)) 空间复杂度:O(1)
2.KMP算法
概念:KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数(数组)实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) [1] (看不懂看下面的例子)
思想:在子串和主串不匹配,两者前面的元素一定相同
- 如果主串当前位置前n个和子串的前n个相同,让子串以O(1)时间复杂度返回到相同部分的下一个位置
结合子串和主串前面元素相同,实际找子串的前n项和后n项也是一样效果
1.next数组
next数组:next【0】设置为-1,是否能在当前位置前面找到两个以第一个元素开始,(当前位置-1)元素结束的字符串,如果找得到当前位置的next【i】为第一个字符串最后一个元素(子串下标是-1的,next[i]==2, 子串实际是第3个元素);找不到next【i】=0(子串下标-1,实际是第一个元素);
2.代码中如何实现next数组;
- next【0】=-1,next【1】=0(前面只有一个元素);
- 当前位置i,如果前一个位置,子串【i-1】等于子串【next【i-1】】,当前位置next【i】=next[i-1]+1;
- 子串【i-1】不等于子串【next【i-1】】,重复这个过程,一直没有等于最后会到第一个元素,next【第一个元素】值为-1,当前位置next【i】=-1+1;
3.代码实现
- KMP算法在字符串匹配中的时间复杂度:O(N) 空间复杂度:O(N)
#include<iostream>
#include<string>
#include<vector>
using namespace std;
void BuildNextFunc(vector<int> &next, string &sub)
{
next[0] = -1;
if(sub.size() > 1)
next[1] = 0;
int i = 2;//下一项
int k = 0;//前一项的K
while(i < sub.size())//next数组还没有遍历完
{
if((k == -1) || sub[k] == sub[i-1])//
{
next[i] = k+1;
i++;
k++;//k = k+1???//下一个K的值新的K值
}
else
{
k = next[k];
}
}
}
int KMP(string &str, string &sub)
{
if(str.empty() || sub.empty())
return -1;
//构建next数组
int n = sub.size();
vector<int> next(n, 0);
BuildNextFunc(next, sub);
int subCur = 0, strCur = 0;
while(strCur < str.size() && subCur < n)
{
if(subCur == -1 || sub[subCur] == str[strCur]){
subCur++;
strCur++;
}
else
{
subCur = next[subCur];
}
}
if(subCur >= n)
return strCur - subCur;
return -1;
}
int main()
{
cout<<KMP("ababcabcdabcde","abcd")<<endl;
cout<<KMP("ababcabcdabcde","")<<endl;
cout<<KMP("ababcabe","abcd")<<endl;
return 0;
}
4.next数组优化
next 数组的优化,即如何得到 nextval 数组:有如下串: aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7. 而修正后的数组 nextval 是-1, -1, -1, -1, -1, -1, -1, -1, 7;
伪代码:sub[i]==sub[ next[i] ],nextval[i]=nextval[ next[i] ]
为什么可行:KMP算法就是如果不相同子串到next【i】去,然后继续比较,当前位置是i,变到next【i】,i位置就是因为值和主串的值不同才到next【i】位置的,现在i和next【i】的值相同,还得比较后继续next【i】到next【next【i】】