0459.重复的子字符串
难度:🟢 简单
标签:字符串
、字符串匹配
链接:459. 重复的子字符串
题目描述
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba"
输出: false
示例 3:
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
解题思路
核心思想
本题的核心思想是,如果一个字符串 s
可以由其子串重复构成,那么这个子串必然是 s
的一个前缀,并且 s
的长度必须是该子串长度的整数倍。
思路选择
枚举法 是解决此问题最直观的思路。我们可以遍历所有可能构成重复单元的子串长度。
可能的子串长度
i
从 1 到s.length / 2
。对于每个长度
i
,我们首先检查s
的总长度是否能被i
整除。如果不能,则该长度的子串不可能构成s
。如果可以整除,我们就取出长度为
i
的前缀作为候选子串,然后检查用这个子串重复s.length / i
次后,得到的新字符串是否与原字符串s
相等。
关键步骤
循环遍历:从
i = 1
循环到s.length / 2
,i
代表候选子串的长度。检查整除:判断
s.length
是否能被i
整除。如果不能,继续下一次循环。构建与比较:如果可以整除,取出前缀
sub = s.substring(0, i)
,然后使用sub.repeat(s.length / i)
构建重复字符串,并与原字符串s
比较。返回结果:如果比较相等,说明找到了一个重复的子串模式,直接返回
true
。如果循环结束后仍未找到,则返回false
。
代码实现 (推荐)
/**
* @param {string} s
* @return {boolean}
*/
var repeatedSubstringPattern = function(s) {
const n = s.length;
// i 是子串的长度
for (let i = 1; i * 2 <= n; ++i) {
// 如果长度能被整除
if (n % i === 0) {
const sub = s.substring(0, i);
// 将子串重复相应次数,看是否与原串相等
if (sub.repeat(n / i) === s) {
return true;
}
}
}
return false;
};
原始代码分析 (用户提供)
您提供的代码采用了正确的枚举法思路,逻辑清晰,但可以通过使用内置方法使代码更简洁。
/**
* @param {string} s
* @return {boolean}
*/
var repeatedSubstringPattern = function (s) {
// i 是候选子串的长度
for (let i = 1; i <= s.length / 2; i++) {
const subStr = s.substr(0, i);
// 检查长度是否为约数
if (s.length % subStr.length === 0) {
const times = s.length / subStr.length;
let joinStr = subStr;
// 手动循环拼接字符串
for (let i = 1; i < times; i++) { // 注意:这里的 i 遮蔽了外层循环的 i
joinStr += subStr;
}
if (joinStr === s) return true;
}
}
return false;
};
优点:正确实现了核心算法逻辑。
待改进:
手动循环拼接字符串
joinStr += subStr
不如使用内置的String.prototype.repeat()
方法简洁和高效。内外层循环都使用了
i
作为变量名,虽然在let
作用域下不会出错,但容易引起混淆,建议使用不同的变量名。
复杂度分析
时间复杂度:O(n√n) 虽然外层循环看起来是 O(n),但内层的字符串构建和比较也是 O(n)。不过,只有当子串长度
i
是n
的约数时才会执行内层操作。一个数n
的约数个数远小于n
,大约是n
的1/3
次方或更小,因此总时间复杂度远优于 O(n²),更接近 O(n√n)。空间复杂度:O(n) 在比较时,需要构建一个与原字符串等长的新字符串,因此空间消耗是 O(n)。
相关题目
总结
本题是字符串处理的入门题,主要考察对问题进行分解和枚举的能力。枚举子串长度的思路非常直观。此外,本题还有一个非常巧妙的数学解法:将字符串 s
与自身拼接得到 s+s
,移除首尾字符后,如果新的字符串中仍然包含原始的 s
,则说明 s
是由重复子串构成的。例如 (s+s).substring(1, s.length * 2 - 1).includes(s)
。