持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
一、题目描述:
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba"
输出: false
示例 3:
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
提示:
- 1 <= s.length <= 10^4
- s 由小写英文字母组成
二、思路分析:
-
使用KMP算法求出来next数组
next[i] 表示模式串中的子串 [0, i] 中相同前后缀中前缀的结尾索引。 -
最长相等前后缀的长度为: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)