记一下刷到哪了,推:代码随想录
4.字符串
难度 | 题目 | 类型 ( 空间 + 时间复杂度 ) |
---|---|---|
简单 | 344.反转字符串 | 遍历 O(1)+O(n) |
简单 | 541. 反转字符串II | 遍历 O(1)+O(n) |
简单 | 05.替换空格 | 遍历 O(n)+O(n) |
中等 | 151.翻转字符串里的单词 | 遍历 O(n)+O(n) |
简单 | 58-II.左旋转字符串 | 计数 O(k)+O(n+m) |
中等 | 28. 实现 strStr()(√) | KMP O(m)+O(n+m) |
简单 | 459. 重复的子字符串(√) | 枚举 O(1)+O(nlogn) |
总结:28 和 459 比较有意思,可以顺便复习KMP,其他的没必要做。
28. 实现 strStr()
字符串匹配,经典 KMP。复习一下(每次都重新学,但是感觉顺畅了很多)。
串1,串2, 在字符串1中找到串2的位置。
KMP的精髓在于,如何利用前面已经匹配上的结果,匹配失败时,不从头匹配,而是从一个特别地位置继续匹配,这取决于串2的结构,所以对匹配串2做处理。
Next [ j ] 表示,当串1中位置 i 和 串2中位置 j 匹配失败时,下一个应该匹配 i 和 串2中的谁。
所以,Next [ j ] 就是,串2中,0 到 j-1,前缀和后缀的最大匹配。
举个例子,串2是 abcabcab,当匹配到最后一个b失败时,说明当前的串1是abcabcaX,再匹配第二个b.
串2 | a | b | c | a | b | c | a | b |
---|---|---|---|---|---|---|---|---|
Next | -1 | 0 | 0 | 0 | 1 | 2 | 3 | 4 |
j 本来已经到了7,匹配失败后, j=4.
串1 | …… | a | b | c | a | b | c | a | X (i) | …… |
---|---|---|---|---|---|---|---|---|---|---|
串2 | a | b | c | a | b | c | a | b (j=7) |
串1 | …… | a | b | c | a | b | c | a | X (i) | …… |
---|---|---|---|---|---|---|---|---|---|---|
串2 | a | b | c | a | b (j=4) |
贴一份板子:
class Solution {
public:
void getNext(string needle, int Next[]) {
int j = 0, k = -1;
int len = needle.size();
Next[0] = -1;
while(j < len-1) {
if(k == -1 || needle[j] == needle[k]) {
j++;
k++;
Next[j] = k;
}
else k = Next[k];// 从前面某个地方再尝试匹配
}
return;
}
int strStr(string haystack, string needle) {
if(needle.size() == 0) return 0;
int len1 = haystack.size(), len2 = needle.size();
int Next[10005] = {0};
getNext(needle, Next);
//for(int i = 0; i < len2; i++) cout << Next[i] << " ";
int i = 0, j = 0;
while(i < len1 && j < len2) {
if(j == -1 || haystack[i] == needle[j] ) {
i++; j++;
}
else{
j = Next[j];
}
if(j == len2) {
return i-j;
}
//cout << i << " " << j << endl;
}
return -1;
}
};
459. 重复的子字符串
1.枚举
偶数次重复一定可以从正中间劈开。
奇数次重复需要枚举重复长度,为小于等于 len/2 的因子。
len/2是个好剪枝。
一般O(nlogn)的复杂度就可以了。
2.KMP
如果满足题意,那么应该从第二个循环节开始,一直往后累加。
所以最后一个位置,如果 s[len-1] == s[next[len-1]],并且,len % ( len-(1+ next[len-1]) ) == 0,就是满足题意的字符串。
否则不是。
len-(1+ next[len-1]) 就是循环节。
有必要说明一下,根据上面的KMP程序,得出的next是这样的。
串 | a | b | c | a | b | e |
---|---|---|---|---|---|---|
Next | -1 | 0 | 0 | 0 | 1 | 2 |
串 | a | b | c | a | b | c |
---|---|---|---|---|---|---|
Next | -1 | 0 | 0 | 0 | 1 | 2 |
因为next是不包含当前位的,是当前位匹配失败后,计算的是前面的最长缀,所以无法区分上面两种情况。
所以需要再加一个判断,看看最后位和它的next位是否相同。