代码随想录算法训练营第十天 | 459. 重复的子字符串、字符串总结、 双指针总结

129 阅读3分钟

459. 重复的子字符串

代码随想录视频讲解

代码随想录文章讲解

暴力解法

  • 列举所有从头开始的substring,和原string对比
class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        for i in range(len(s) // 2 + 1):
            substr = s[:i+1]
            if len(s) % len(substr) == 0 and len(s) != len(substr):
                times = len(s) // len(substr)
                construct_str = ''.join(list(substr)*times)
                if construct_str == s:
                    return True
            else:
                continue
        return False

s是否在(s+s)[1:-1]里?(移动匹配)

Repeated pattern string looks like PatternPattern, and the others like Pattern1Pattern2.

Let's double the input string:

PatternPattern --> PatternPatternPatternPattern Pattern1Pattern2 --> Pattern1Pattern2Pattern1Pattern2

Now let's cut the first and the last characters in the doubled string:

PatternPattern --> *atternPatternPatternPatter* Pattern1Pattern2 --> *attern1Pattern2Pattern1Pattern*

It's quite evident that if the new string contains the input string, the input string is a repeated pattern string.

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

KMP算法

  • 如果pattern存在,最长公共前后缀应该在lps[n-1]的位置(最后一个字符的位置),pattern的长度应该为n - lps[n-1](因为前后缀不能包括第一个和最后一个字符,这样最长前后缀的长度就是总长度减去一个pattern的长度了)
  • 我们只要判断n % len(pattern)是否为0和最长公共前后缀是否存在即可
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        n = len(s)
        if n == 0 or n == 1:
            return False
        if n == 2:
            return s[0] == s[1]
​
        prevLPS = 0
        lps = [0] * n
        for i in range(1, n):
            while prevLPS > 0 and s[i] != s[prevLPS]:
                prevLPS = lps[prevLPS - 1]
            if s[i] == s[prevLPS]:
                prevLPS += 1
            lps[i] = prevLPS
​
        l = lps[n-1]
​
        return l != 0 and n % (n-l) == 0

字符串总结

要不要使用库函数

  • 如果题目可以直接用库函数解决/库函数是解法的主体:不要用
  • 只是解题的一小部分:可以使用

双指针法

  • 在数组、链表和字符串的问题中很常见
  • 其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

反转系列

  • 其实当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章
  • 先整体反转再局部反转可以解决左旋问题

KMP算法

  • 当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。

  • 可以解决两类经典问题:

    • 匹配问题
    • 重复子串问题

双指针总结

数组篇

  • 使用双指针,可以在一个for循环下完成两个for循环的工作,从而将时间复杂度下降

字符串篇

  • 可以用于反转字符串:定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。
  • 可以用于替换空格/填充数组:其实很多数组(字符串)填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

链表篇

  • 快慢指针:使用快慢指针(双指针法),分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

N数之和

  • 双指针法需要原数组排序:

    • > target:right -=1
    • < target:left +=1

总结

  • 除了链表一些题目一定要使用双指针,其他题目都是使用双指针来提高效率,一般是将O(n^2)的时间复杂度,降为O(n)。