【水滴计划 | 每日两题】:左旋转字符串、重复的子字符串

114 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

1、写在前面

大家好,我是翼同学,这里是【水滴计划 | 刷题日志】

每日两题,拒绝摆烂。

2、内容

2.1、题目一:左旋转字符串

链接:剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)

(1) 描述

image.png

(2) 举例

image.png

image.png

(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;
    }
};

image.png

(4) 解释

这里我们不使用额外的空间,而是在原字符串上操作,进而实现左旋转的功能。

具体的解题思路如下所示:

image.png

2.2、题目二:重复的子字符串

链接:459. 重复的子字符串 - 力扣(LeetCode)

(1) 描述

image.png

(2) 举例

image.png

image.png

(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;
    }
};

image.png

(4) 解释

我们先回顾一下前后缀子串的含义:

  • 前缀:指的是不包含最后一个字符的所有以第一个字符开头的连续子串
  • 后缀:指的是不包含第一个字符的所有以最后一个字符结尾的连续子串

如果你忘了KMP算法,可以参考我这篇笔记:

我们知道在KMP算法中,利用next数组可以求得,在每个字符之前的子串中包含的最长相等前后缀子串。

以题目给出的例子,我们来捋一遍思路:

  • 输入: s = "abcabcabcabc"
  • 输出: true
  • 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
  • 字符串如下:

image.png

  • 获取到next数组后,如下所示:

image.png

  • 我们知道,next数组的值,指的是每个字符之前的子串中包含的最长相等前后缀子串
  • 因此,next数组末尾元素的值,加1后就是整个字符串s的最长相等前后缀子串的长度
  • next[n-1] + 1 = 9,即前缀子串 = 后缀子串 = abcabcabc
  • 示意图如下:

image.png

  • 如上图所示:
  • 我们求得字符串s的长度n12
  • 字符串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、写在最后

好了,今天就刷到这里,明天再来。