-
KMP算法
解决字符串匹配问题
文本串(n)
模式串(m)
暴力匹配两层for循环的时间复杂度是O(nm)
KMP的经典思想:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
什么是KMP
由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母叫做KMP
KMP有什么用
KMP主要应用在字符串匹配上,主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
前缀与后缀
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
最长相等前后缀
每个字串的最长相等前后缀
字符串aabaaf子串 | 前缀表 |
a | 0 |
aa | 1 |
aab | 0 |
aaba | 1 |
aabaa | 2 |
aabaaf | 0 |
使用前缀表的匹配过程
找到的不匹配的位置, 此时要看它的前一个字符的前缀表的数值是多少。
前一个字符的前缀表的数值是2, 所以把下标移动到下标2的位置继续比配。
next数组
构造next数组其实就是计算模式串s,前缀表的过程。 主要有如下三步:
- 初始化:定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。
- 处理前后缀不相同的情况
- 处理前后缀相同的情况
28. 实现 strStr()
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
视频讲解:帮你把KMP算法学个通透!(理论篇)_哔哩哔哩_bilibili
文章讲解:代码随想录
class Solution {
public:
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 strStr(string haystack, string needle) {
if(needle.size()==0){
return 0;
}
int next[needle.size()];
getNext(next, needle);
int j = 0;
for(int i=0; i<haystack.size(); i++){
while(j>0 && haystack[i]!=needle[j]){
j = next[j-1];
}
if(haystack[i]==needle[j]){
j++;
}
if(j==needle.size()){
return (i-needle.size()+1);
}
}
return -1;
}
};
459. 重复的子字符串
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
视频讲解:字符串这么玩,可有点难度! | LeetCode:459.重复的子字符串_哔哩哔哩_bilibili
文章讲解:代码随想录
移动匹配解法
当一个字符串s:abcabc,内部由重复的子串组成,那么这个字符串的结构一定是这样的:
也就是由前后相同的子串组成。
那么既然前面有相同的子串,后面有相同的子串,用 s + s,这样组成的字符串中,后面的子串做前串,前面的子串做后串,就一定还能组成一个s,如图:
所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。
当然,在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,而要搜索的是中间拼接出来的s。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string t = s + s;
t.erase(t.begin()); t.erase(t.end() - 1); // 掐头去尾
if (t.find(s) != std::string::npos) return true; // r
return false;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(1)
KMP解法
就是求最长相等前后缀不包含的那个子串
class Solution {
public:
void getNext (int* next, const string& s){
next[0] = 0;
int j = 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;
}
}
bool repeatedSubstringPattern(string s) {
if (s.size() == 0) {
return false;
}
int next[s.size()];
getNext(next, s);
int len = s.size();
if (next[len-1] != 0 && len%(len-(next[len-1])) == 0) {
return true;
}
return false;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)