151.翻转字符串里的单词
题目
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
尝试解答
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
words = s.split()
left=0
right=len(words)-1
while left<right:
words[left],words[right]=words[right],words[left]
left+=1
right-=1
return " ".join(words)
代码随想录的代码
class Solution:
def reverseWords(self, s: str) -> str:
# 删除前后空白
s = s.strip()
# 反转整个字符串
s = s[::-1]
# 将字符串拆分为单词,并反转每个单词
s = ' '.join(word[::-1] for word in s.split())
return s
split函数是拆分字符串的,strip函数是去除空白字符。
卡码网:55.右旋转字符串
题目
字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
代码
k = int(input())
s = input()
print(s[-k:] + s[:-k])
28. 实现 strStr()
题目
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle
不是 haystack
的一部分,则返回 -1
代码随想录学习-KMP算法
KMP算法的核心思想是通过预处理模式字符串,构建一个部分匹配表,该表记录了模式字符串自身的重复结构。在匹配过程中,当出现字符不匹配时,利用部分匹配表可以避免重复比较已经匹配好的字符。即KMP算法主要应用在字符串匹配上。
前缀表:用来回退,记录了模式串与主串不匹配时,模式串应该从哪里重新匹配。记录下标之前(包括下标)的字符串中,有多大长度的相同前缀后缀。
最长公共前后缀
前缀是指包含第一个字符但不包括最后一个字符的连续子字符串
后缀是指包含最好一个字符但不包括首字符的连续子字符串
最长公共前后缀就是前后缀字节数相同且最长。
步骤:
1.初始化:定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。
2.处理前后缀不相同的情况
3.处理前后缀相同的情况
4.更新next
学习复述
KMP算法就是利用前缀表来找匹配字符串。它是从前面已经匹配好的开始。例如主串为aabaabaafa,模式串为aabaaf,当f不匹配,看前面aabaa,它的前缀和后缀完全相同的是aa。所以从后缀的前一个字母开始匹配,即从b再匹配。在写代码中关键是要找到这个最长公共前后缀的长度,这样就可以知道从哪里开始匹配。而具体代码思路,首先需要两个指针i和j,i指向后缀末尾,j指向前缀末尾。同时j也表示最长公共前后缀的长度。next表示每个子字符串的最长前后缀长度。首先是初始化,那么next初始化为0。因为对于第一个字母,前后缀为0。j前缀末尾初始化为0。对于个数为2的子字符串,这样前缀末尾是0,后缀末尾为1。j,i即初始化为0,1。接着分为两种情况,一是前后缀相同,j加1后付给next,i也加1。二是前后缀不相同,前缀末尾往前移直至为0后将0付给next。以上为大致思路。下面看具体代码:
代码1-前缀表-1
class Solution:
def getNext(self, next, s):
j = -1
next[0] = j
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j+1]:
j = next[j]
if s[i] == s[j+1]:
j += 1
next[i] = j
def strStr(self, haystack: str, needle: str) -> int:
if not needle:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = -1
for i in range(len(haystack)):
while j >= 0 and haystack[i] != needle[j+1]:
j = next[j]
if haystack[i] == needle[j+1]:
j += 1
if j == len(needle) - 1:
return i - len(needle) + 1
return -1
代码2-前缀不减1
class Solution:
def getNext(self, next: List[int], s: str) -> None:
j = 0
next[0] = 0
for i in range(1, len(s)):
while j > 0 and s[i] != s[j]:
j = next[j - 1]
if s[i] == s[j]:
j += 1
next[i] = j
def strStr(self, haystack: str, needle: str) -> int:
if len(needle) == 0:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = 0
for i in range(len(haystack)):
while j > 0 and haystack[i] != needle[j]:
j = next[j - 1]
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return i - len(needle) + 1
return -1
然后对于strStr函数,就是当haystack中的字母与needle字母不相同时,needle里的字母跳到next里的前一个,然后继续比对,相等的话就跳到下一个。直至将整个needle遍历完,输出匹配的索引。
459.重复的子字符串
题目
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
尝试解答
感觉这个也是可以用KMP算法的,但是由next数组判断该字符串是否由子字符串组成还是不会写,还是看一下代码随想录吧
代码随想录的代码
代码
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
if len(s) == 0:
return False
nxt = [0] * len(s)
self.getNext(nxt, s)
if nxt[-1] != 0 and len(s) % (len(s) - nxt[-1]) == 0:
return True
return False
def getNext(self, nxt, s):
nxt[0] = 0
j = 0
for i in range(1, len(s)):
while j > 0 and s[i] != s[j]:
j = nxt[j - 1]
if s[i] == s[j]:
j += 1
nxt[i] = j
return nxt
假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。
因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,(这里如果不懂,看上面的推理)
所以如果 nx % (n - m)x = 0,就可以判定有重复出现的子字符串。
以上是代码随想录的推导,我觉得我的一个错误在于在推这个判断公式的时候我是有点凑数的感觉,看怎么凑才能使得各个数值对的上。但其实应该直接推导。
双指针回顾
数组
在移除数组中,为了提高效率,利用快慢指针进行数组的覆盖。
字符串
在反转字符串时,定义左右指针向中间移动,交换元素
链表
在反转链表中,只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。
而在链表中求环,使用快慢指针,分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
N数之和
对于三数、四数之和,哈希表不适用。因为去重比较困难,而利用指针就直接可以将重复元素跳过。通过前后两个指针不算向中间逼近,在一个for循环下完成两个for循环的工作。四数之和,其实思路是一样的,就是三数之和的基础上再套一层for循环。