题目描述
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成。
示例 2:
输入: s = “aba”
输出: false
示例 3:
输入: s = “abcabcabcabc”
输出: true
解释: 可由子串 “abc” 重复四次构成。 (或子串 “abcabc” 重复两次构成。)
提示:
- 1 <= s.length <= 104
- s 由小写英文字母组成
解题思路
首先想到的是用暴力法,遍历所有可能的子串长度,然后检查是否可以通过重复该子串构成原字符串。具体实现时,可以从长度为 1 的子串开始,逐渐增加长度,直到字符串的一半。因为如果子串长度超过一半,就不可能重复构成原字符串。
1 | func repeatedSubstringPattern(s string) bool { |
上面的代码中,做了一些情况的优化,比如如果字符串长度不能被子串长度整除,就直接跳过该长度的子串检查。这样可以减少不必要的比较。
这种方法的时间复杂度是 O(n^2)
,但其实提交也能通过。
优化思路
看了题解后,发现还有一个思路,可以通过字符串的拼接来判断是否存在重复的子串。具体思路是将字符串 s
拼接成 s + s
,然后去掉首尾各一个字符,检查剩下的字符串中是否包含原字符串 s
。
1 | func repeatedSubstringPattern(s string) bool { |
要理解这种方法,就需要知道一个重要的性质:如果一个字符串是由重复子串构成的,那么将该字符串进行若干次移位后可以得到原字符串。例如,对于字符串 s = "abcabc"
:将其移位后可以得到 "bcabca"
、"cabcab"
、"abcabc"
。
基于这个思考,可以构建一个新的字符串 s + s
,比如:s
为 abcabc
,那么 s+s
为 "abcabcabcabc"
。s
的所有移位都可以在这个新字符串中找到(bcabca
、cabcab
、abcabc
)。当然 ss
是由两个 s
组成的,所以包含了 s
本身,如果去掉首尾各一个字符后,仍然能找到 s
,那么就说明 s
是由重复子串构成的。
这种方法的时间复杂度是 O(n)
,空间复杂度也是 O(n)
。