六、字符串
1.字符串匹配问题 – KMP
KMP算法是一种改进的字符串匹配算法。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的,消除了指针i的回溯问题。KMP算法的时间复杂度O(m+n)。设主串为Str
,模式串为Pat
,以
Str=BBC ABCDAB ABCDABCDABDE Pat=ABCDABD 为例,介绍KMP算法。
Step1: 寻找模式串Pat
中各个子串的前缀后缀最长公共元素长度
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a5cda35a45d5e648b35c5ab840985203.png)
Next
数组,
Next
数组相当于最大长度表整体向右移动一位,然后初始值赋值为-1。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8da12822e8f2c6f9083333fe55774536.png)
vector<int> getnext(string needle){
int len=needle.size();
vector<int>next;
next.push_back(-1); //next数组的首位为-1
int slow=-1; //slow指向子串的当前匹配的前缀位置
int quick=0; //quick指向子串的当前匹配的后缀位置 如:ABCBA,slow指向第一个A,quick指向最后一个A
//ABCDAB,slow指向第二个索引B,quick指向最后一个索引B
while(quick<len){
if(slow==-1 || needle[slow]==needle[quick]){
slow++; //slow的值就是最大公共元素长度
quick++;
next.push_back(slow);
}else
slow=next[slow];
}
return next;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/063b0f80b1b249aa5d968505e7946dfb.png)
Step3: 根据Next数组进行字符串匹配:
假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置。
(1).如果
j=-1
,或者当前字符匹配成功(即Str[i] == Pat[j]),都令
i++,j++
,继续匹配下一个字符;
(2).如果
j !=-1
,且当前字符匹配失败(即S[i] != P[j]),则令
i
不变,
j = Next[j]
。此举意味着失配时,模式串P相对于文本串S向右移动了
j-Next[j]
位。换言之,当匹配失败时,
模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,
即移动的实际位数为:j-next[j]
,且此值大于等于1。 如下图所示:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a6ccdbd21ccbb31b367330b840ed4872.png)
class Solution {
public:
vector<int> getnext(string needle){ //计算Next数组
vector<int> next;
int slow=-1;
int quick=0;
next.push_back(-1);
while(quick<needle.size()){
if(slow==-1 || needle[slow]==needle[quick]){
slow++;
quick++;
next.push_back(slow);
}else
slow=next[slow];
}
}
//KMP算法
int strStr(string haystack, string needle) {
if(needle.empty())
return 0;
int len1=haystack.size();
int len2=needle.size();
vector<int>next; //next数组
next=getnext(needle); //获取next数组
int i=0;
int j=0;
while(i<len1 && j<len2){
if(j==-1 || haystack[i]==needle[j]){
i++; //主串Str指针
j++; //模式串Pat指针
}else{
j=next[j]; //当前字符匹配失败,i不变,j=next[j]
}
}
if(j==len2)
return i-j;
return -1;
}
};