持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情
1、写在前面
大家好,我是翼同学,这里是【水滴计划 | 刷题日志】
每日两题,拒绝摆烂。
2、内容
2.1、题目一:左旋转字符串
链接:剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)
(1) 描述
(2) 举例
(3) 解题
参考代码如下:
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(), s.begin() + n);
reverse(s.begin() + n, s.end());
reverse(s.begin(), s.end());
return s;
}
};
(4) 解释
这里我们不使用额外的空间,而是在原字符串上操作,进而实现左旋转的功能。
具体的解题思路如下所示:
2.2、题目二:重复的子字符串
链接:459. 重复的子字符串 - 力扣(LeetCode)
(1) 描述
(2) 举例
(3) 解题
这道题要求在字符串s中查找是否出现过某段子串p,使得该字符串s由这个子串p重复多次而构成。在一个串中查找是否出现过另一个串,我们很自然的想到了KMP算法。
那这道题如何用KMP算法来求解呢?
先给出参考代码:
class Solution {
public:
// 自定义函数,用于获取next数组
void getNext (int* next, const string& p){
next[0] = -1;
int j = -1;
for(int i = 1;i < p.length(); i++){
while(j >= 0 && p[i] != p[j + 1]) {
j = next[j];
}
if(p[i] == p[j + 1]) {
j++;
}
next[i] = j;
}
}
// 主函数
bool repeatedSubstringPattern (string s) {
int n = s.size(); // 获取主串 s 的长度为 n
if (n == 0) return false; // 特殊情况的处理
int next[n]; // 定义一个next数组
getNext(next, s); // 对next数组元素进行赋值
int p = n - (next[n - 1] + 1); // 重复子串的长度为 p
// 如果next数组的末尾元素不等于-1
// 并且重复子串的长度 p 刚好可以被字符串 s 的长度整除
// 则说明主串可以被重复子串构成
if (next[n - 1] != -1 && n % x == 0) {
return true;
}
return false;
}
};
(4) 解释
我们先回顾一下前后缀子串的含义:
- 前缀:指的是不包含最后一个字符的所有以第一个字符开头的连续子串
- 后缀:指的是不包含第一个字符的所有以最后一个字符结尾的连续子串
如果你忘了KMP算法,可以参考我这篇笔记:
我们知道在KMP算法中,利用next数组可以求得,在每个字符之前的子串中包含的最长相等前后缀子串。
以题目给出的例子,我们来捋一遍思路:
- 输入: s = "abcabcabcabc"
- 输出: true
- 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
- 字符串如下:
- 获取到
next数组后,如下所示:
- 我们知道,
next数组的值,指的是每个字符之前的子串中包含的最长相等前后缀子串 - 因此,
next数组末尾元素的值,加1后就是整个字符串s的最长相等前后缀子串的长度 next[n-1] + 1 = 9,即前缀子串 = 后缀子串 =abcabcabc- 示意图如下:
- 如上图所示:
- 我们求得字符串
s的长度n为12 - 字符串
s的最长相等前后缀为"abcabcabc",其长度为next[n-1] + 1,也就是9 - 设
p等于最长相等前后缀不包含的子串的长度 - 则
p等于n - (next[n-1] + 1),即12-9=3 - 又因为
n % p == 0,刚好整除 - 此时就已经可以说明字符串
s由重复的子串p构成
我们得出结论:
- 如果字符串s是由多个子串p重复构成的
- 那么最长相等前后缀不包含的子串就是最小重复子串
3、写在最后
好了,今天就刷到这里,明天再来。