1 KMP知识点复习+套路总结
1.1 前缀:
对一个字符串,包含首字母,不包含尾字母的所有子串。
e.g. aabaaf的前缀:a,aa,aab,aaba,aabaa
1.2 后缀:
对一个字符串,包含尾字母,不包含首字母的所有子串。
e.g. aabaaf的后缀:f,af,aaf,baaf,abaaf
1.3 最长相等前后缀:
对于一个串,前缀和后缀一样的最长的子串
e.g.
串 最长相等前后缀长度【前缀表 = next数组】
a 0
aa 1
aab 0
aaba 1
aabaa 2
aabaaf 0
1.4 如何利用前缀表(next数组)来匹配?
next数组的含义:
(1)记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
(2)当主串的第i个元素和模式串的第j个元素失配时,主串的第i个应该和next[ j-1 ] 匹配。
下标 012345678
文本串:aabaabaaf
下标 012345
模式串:aabaaf
010120
所以当文本串第5元素b和模式串的第5元素f不匹配了,模式串就回退到next[5 - 1] = 2,即模式串的下标为2 的b继续与文本串的下标为5的b继续匹配。
1.5 构造next数组的套路代码
next数组的几种写法:
【1】 = 正常的最长相等前后缀的 (用这个)
【2】正常的最长相等前后缀的全部后移1位,第0号元素设为-1
【3】= 正常的最长相等前后缀的每一位减1
步骤:
设两个指针,i 表示后缀的末尾,j 表示前缀的末尾
【1】初始化
【2】处理前后缀不相同的情况
【3】处理前后缀相同的情况
【4】更新next[]
代码模板:
vector<int> next(needle.size());
int j = 0; //初始化 j表示前缀的最后一位 i表示后缀的最后一位
next[0] = 0;
for(int i = 1; i < needle.size();i++)
{
while(j > 0 && needle[i] != needle[j])j = next[j - 1]; //前后缀不相同回退
if(needle[i] == needle[j])j++; //前后缀相同
next[i] = j; //求next[]
}
2 KMP-题1
KMP标准题,复习了carl的视频和讲义才写的。
class Solution {
public:
int strStr(string haystack, string needle)
{
// 字符串匹配 KMP 但忘差不多了,决定看完视频再回来写!
// [1]获得模式串needle的next[]
vector<int> next(needle.size());
int j = 0; //初始化 j表示前缀的最后一位 i表示后缀的最后一位
next[0] = 0;
for(int i = 1; i < needle.size();i++)
{
while(j > 0 && needle[i] != needle[j])j = next[j - 1]; //前后缀不相同回退
if(needle[i] == needle[j])j++; //前后缀相同
next[i] = j; //求next[]
}
j = 0; //j遍历模式串
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;
}
};
3 KMP-题2
KMP变形题目。
【我的尝试】第一时间想法,思路:发现true的好像next[]都有点规律! 0*n(n*k递增序列)。失败了
class Solution {
public:
bool repeatedSubstringPattern(string s)
{
if(s.size() % 2 != 0)return 0; //奇数的肯定不行
vector<int> next(s.size());
int j = 0;
next[0] = 0;
cout << next[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;
cout << next[i] << ' ';
}
// 判断: 前面是k个0 + k倍的递增序列
int cnt = 0;
int first = 0;
int k = 1;
for(int i = 0; i < s.size();i++)
{
while(first == 0 && next[i] == 0)
{
i++;
cnt++;
}
first = 1;
if(i >= s.size() || first && next[i] == 0)return 0;
if(next[i] != k)return 0;
k++;
}
if(next[s.size()-1] % cnt != 0)return 0;
return 1;
}
};
但是在第79个点发现错了,
"abaababaab"的next是0 0 1 1 2 3 2 3 4 5
不符合我猜的规律!!
【方法2】暴力。左边界是固定的,所以遍历右边界即可。O(n^2)AC:
class Solution {
public:
bool repeatedSubstringPattern(string s)
{
int l = 0;
for(int i = 0; i < s.size() / 2;i++) //i表示右边界
{
int len = i - l + 1;
if(s.size() % len != 0)continue;
string tmp = s.substr(l,len);
string str = tmp;
for(int k = 1; k < s.size()/len; k++)tmp += str;
if(tmp == s)return 1;
}
return 0;
}
};
正确解法:todo
字符串章的总结
双指针问题的总结
总结
这次就复习kmp画的时间比较长!3h。