[更文刷题] 459. 重复的子字符串

101 阅读2分钟

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

一、题目描述:

459. 重复的子字符串

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

示例 1:

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

输入: s = "aba"
输出: false

示例 3:

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

提示:

  • 1 <= s.length <= 10^4
  • s 由小写英文字母组成

二、思路分析:

  1. 使用KMP算法求出来next数组
    next[i] 表示模式串中的子串 [0, i] 中相同前后缀中前缀的结尾索引。

  2. 最长相等前后缀的长度为:next[len - 1] + 1。
    数组长度为:len。

    • 经过对所有重复字符串的前缀表进行验证,他们的前缀表最后一位都不会为-1,所以如果next[len - 1] == -1 ,就说明s是不重复的。
    • 如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。
    • 数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。

三、AC 代码:

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if (s.length() == 0) {
            return false;
        }

        char[] chars = s.toCharArray();
        int len = chars.length;

        int[] next = getNext(chars, len);

        int maxLen = next[len - 1] + 1;

     
        return next[len - 1] != -1 && len % (len - maxLen) == 0;
    }

    /**
     * 获取匹配串的next数组
     *
     * @param needle
     * @param len
     * @return
     */
    int[] getNext(char[] needle, int len) { 
        int[] next = new int[len];

        next[0] = -1;
        int k = -1;

        for (int i = 1; i < len; i++) {
            while(k != -1 && needle[k + 1] != needle[i]) {
                k = next[k];
            }

            if (needle[k + 1] == needle[i]) {
                k++;
            }

            next[i] = k;
        }

        return next;
    }
}

参考:

非KMP(不会KMP)O(n)时间复杂度解法,官方暴力优化版本 - 重复的子字符串 - 力扣(LeetCode)

ACM 选手图解 LeetCode 重复的子字符串 | 编程文青李狗蛋 - 重复的子字符串 - 力扣(LeetCode)