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 likePattern1Pattern2.Let's double the input string:
PatternPattern-->PatternPatternPatternPatternPattern1Pattern2-->Pattern1Pattern2Pattern1Pattern2Now 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)。