《代码随想录》第四章 字符串 459. 重复的子字符串

《代码随想录》第四章 字符串 459. 重复的子字符串

努力学习!

题目:力扣链接

  • 给定一个非空的字符串 s​ ,检查是否可以通过由它的一个子串重复多次构成。

一、思想

这道题目的核心思想是判断一个字符串是否可以由它的一个子串重复多次构成。我们可以通过KMP​算法中的next数组来解决这个问题。具体来说,如果一个字符串可以由一个子串重复多次构成,那么它的next数组会有特定的性质:我们构建next数组,字符串长度减去最长相同前后缀长度如果包含字串即能被字符串长度整除,证明含有重复字符串。


二、代码

class Solution
{
public:
    // bool repeatedSubstringPattern(string s)
    // {
    //     string t = s + s;
    //     t.erase(t.begin());
    //     t.erase(t.end() - 1);
    //     if (t.find(s) != s.size()) {
    //         return true;
    //     }
    //     return false;
    // }

    /**
     * 获取字符串s的next数组
     * next数组用于KMP算法,next[i]表示s[0..i]的最长前后缀长度
     * @param next next数组的指针
     * @param s 输入字符串
     */
    void getNext(int *next, const string &s)
    {
        int j = -1;  // 初始化j为-1
        next[0] = j; // next[0]为-1,表示空字符串与任何字符串的最长前后缀长度为0
        for (int i = 1; i < s.size(); ++i) {
            // 当j >= 0且s[i] != s[j + 1]时,j递减
            while (j >= 0 && s[i] != s[j + 1]) {
                j = next[j];
            }
            // 当s[i] == s[j + 1]时,j递增
            if (s[i] == s[j + 1]) {
                j++;
            }
            next[i] = j; // 更新next[i]
        }
    }

    /**
     * 判断字符串s是否为重复子串模式
     * 若s为重复子串模式,返回true,否则返回false
     * @param s 输入字符串
     * @return 若s为重复子串模式,返回true,否则返回false
     */
    bool repeatedSubstringPattern(string s)
    {
        if (s.size() == 0) {
            return false; // 空字符串不为重复子串模式
        }

        vector<int> next(s.size(), 0); // 初始化next数组
        getNext(next.data(), s);       // 获取s的next数组
        int len = s.size();            // 获取字符串长度
        // 当next[len - 1]不为-1且len能被len - next[len - 1] - 1整除时,s为重复子串模式
        if (next[len - 1] != -1 && len % (len - next[len - 1] - 1) == 0) {
            return true;
        }
        return false;
    }
};

三、代码解析

  1. KMP算法中的next数组:next数组用于记录字符串中每个位置的最长相同前后缀的长度。例如,对于字符串 "abab"​,next数组为 [-1, 0, 1, 2]​,表示:

    • 第0个字符 'a'​ 的最长前后缀长度为0(空字符串)。
    • 第1个字符 'b'​ 的最长前后缀长度为0。
    • 第2个字符 'a'​ 的最长前后缀长度为1("a"​)。
    • 第3个字符 'b'​ 的最长前后缀长度为2("ab"​)。
  2. 重复子串的判断:如果一个字符串 s​ 可以由一个子串重复多次构成,那么它的next数组的最后一个元素 next[len - 1]​ 会有以下性质:

    • next[len - 1]​ 不等于 -1​,表示字符串中存在相同的前后缀。
    • 字符串的长度 len​ 可以被 len - next[len - 1] - 1​ 整除,这意味着字符串可以被一个长度为 len - next[len - 1] - 1​ 的子串重复构成。

2. 关键点说明

  • next数组的性质:next数组的最后一个元素 next[len - 1]​ 表示整个字符串的最长相同前后缀的长度。如果 next[len - 1]​ 不等于 -1​,说明字符串中存在一个相同的前后缀。
  • 重复子串的长度:如果字符串可以由一个子串重复构成,那么这个子串的长度就是 len - next[len - 1] - 1​。例如,对于字符串 "ababab"​,next数组为 [-1, 0, 1, 2, 3, 4]​,len - next[len - 1] - 1 = 6 - 4 - 1 = 1​,即子串 "a"​ 可以重复构成 "ababab"​。
  • 整除条件:如果字符串的长度 len​ 可以被子串的长度 len - next[len - 1] - 1​ 整除,说明字符串可以由这个子串重复构成。

四、复杂度分析

  • 时间复杂度O(n)

    • getNext​ 函数的时间复杂度为 O(n),其中 n​ 是字符串的长度。
    • repeatedSubstringPattern​ 函数的时间复杂度为 O(n),因为它主要依赖于 getNext​ 函数。
    • 总体时间复杂度为 O(n)。
  • 空间复杂度O(n)

    • 使用了next数组,空间复杂度为 O(n)。

白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》

你可能感兴趣的:(C++,字符串)