重复的子字符串

标签: 字符串 字符串匹配

难度: Easy

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

Submission

运行时间: 25 ms

内存: 16.1 MB

class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        doubled_str = s + s
        sliced_str = doubled_str[1:len(doubled_str) - 1]
        return s in sliced_str

Explain

这个题解的思路是,如果字符串 s 可以由一个子串重复多次构成,那么将 s 和自身拼接成一个新字符串 doubled_str,并去掉 doubled_str 的首尾字符得到 sliced_str,那么原字符串 s 一定会作为子串出现在 sliced_str 中。反之,如果 s 不能由一个子串重复多次构成,那么 s 就不会出现在 sliced_str 中。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        # 将字符串 s 与自身拼接
        doubled_str = s + s
        # 去掉拼接后字符串的首尾字符
        sliced_str = doubled_str[1:len(doubled_str) - 1]
        # 判断原字符串 s 是否作为子串出现在 sliced_str 中
        return s in sliced_str

Explore

这种方法的原理基于字符串的周期性。如果字符串s可以由其子串t重复多次构成,那么将s和自身拼接后,形成的新字符串doubled_str中至少包含两个完整的s,即形如s + s = t...t + t...t。去掉首尾字符后,s的每个重复周期在新字符串sliced_str中仍然存在,只是首尾各少了一部分。因此,如果s在sliced_str中可以找到,则说明s具有周期性,即可以由更短的子串重复组成。反之,如果s不具有这样的重复结构,那么s不会在sliced_str中完整出现。

去掉首尾字符主要是为了消除边界影响。例如,如果字符串s是由子串t重复构成的,去掉首尾字符后,虽然首尾可能会丢失部分重复的子串t,但由于doubled_str中包含至少两个s,这种丢失不会影响中间部分的s的完整出现。因此,这种操作不会导致s的重复模式在sliced_str中丢失,只要s是由子串重复构成的,它仍然会在sliced_str中出现。

对于非常短的字符串,这种方法同样有效。例如,如果s = 'a',则doubled_str = 'aa',去掉首尾字符后变为空字符串,s显然不会出现在空字符串中,因此返回false。而对于s = 'aa',则doubled_str = 'aaaa',去掉首尾字符后得到'aa',s在sliced_str中完整地出现了,因此返回true。这说明无论字符串s的长度如何,该方法都能正确判断s是否由其子串重复构成。

在判断s是否出现在sliced_str中时,考虑了所有可能的出现位置。使用Python的'in'操作符来检查s是否为sliced_str的子串,这个操作会检查sliced_str的所有可能位置以确定s是否为其子串。因此,这种方法不局限于检查特定位置,而是全面地检查整个sliced_str。