topic30:串联所有单词的子串
题目描述:
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd","cdabef", "cdefab","efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。
思路:word所有元素都在子串中,先判断word[i]是否在子串,不在则右移,在的话就以长度len(word[i])为一组,依次匹配word,匹配成功,将下标加入结果列表中,使用滑动窗口的思想
def findSubstring(s, words):
n = len(s) # 字符串 s 的长度
m = len(words) # words 列表中元素的个数
w_l = len(words[0]) # 子串的长度
window_size = m * w_l # 窗口大小
cnt = {word: 0 for word in words} # 初始化 cnt 字典,用于记录子串中每个元素出现的次数
words_cnt = cnt.copy() # 复制 cnt 字典,用于记录 words 列表中每个元素出现的次数
for word in words:
words_cnt[word] += 1
start = 0 # 窗口起始位置
ans = [] # 储存结果
while start < n - window_size + 1: # 窗口不能超过字符串 s 的长度
temp = cnt.copy() # 复制 cnt 字典,用于记录当前窗口中每个元素出现的次数
for i in range(start, start + window_size, w_l):
part = s[i: i + w_l] # 截取子串
if part not in temp: # 如果子串不在 words 中,则窗口不匹配,退出循环
break
else:
temp[part] += 1 # 否则将子串出现的次数加 1
if temp[part] > words_cnt[part]: # 如果子串出现的次数超过了 words 中该元素出现的次数,退出循环
break
else: # 如果内层循环正常结束,则说明窗口匹配,将窗口起始位置加入结果列表
ans.append(start)
start += 1 # 窗口起始位置向右移动 1 个单位
return ans # 返回结果列表
topic31:下一个排列
题目描述:
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
思路:如果整个数组是降序,则返回升序数组,对一个数组,先将最后面的两个元素记录,若为升序,直接交换最后两个元素,若为降序,从后面加入一个元素,如果加入后不是降序,则重新排序
def nextPermutation(nums):
if nums == sorted(nums)[::-1]: # 如果数组已经是降序排列,则返回升序排列
return sorted(nums)
temp = nums[-1:-3:-1][::-1] # 选取最后两个元素,按顺序排列
if temp[0] < temp[1]: # 如果最后两个元素是升序,直接交换两个元素的位置
nums[-1] = temp[0]
nums[-2] = temp[1]
return nums
else:
i = -2
while temp == sorted(temp)[::-1]: # 当基准列表是降序时,加入元素,直到不是降序
i -= 1
temp.insert(0, nums[i])
temp1 = sorted(temp) # 对加入元素后的列表进行升序排列
bigger = temp1.index(temp[0]) + 1 # 找到比基准元素大的最小元素
result = [temp1[bigger]]
del temp1[bigger]
result = result + temp1
nums[i:] = result # 将后面的元素替换
return nums
topic33:搜索旋转排序数组
题目描述:
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题
思路:由于要求时间复杂度为O(logn),所以需要使用二分法,将数组一分为二,其中必定有一个有序,有一个无序,对无序部分再二分,循环直到找出范围。
class Solution:
def search(self, nums: List[int], target: int) -> int:
"""用二分法,先判断左右两边哪一边是有序的,再判断是否在有序的列表之内"""
if len(nums) <= 0:
return -1
left = 0
right = len(nums) - 1
while left < right:
mid = (right - left) // 2 + left
if nums[mid] == target:
return mid
# 如果中间的值大于最左边的值,说明左边有序
if nums[mid] > nums[left]:
if nums[left] <= target <= nums[mid]:
right = mid
else:
# 如果不在左边有序列表中,则在右边无序列表中查找,因此更新左指针
# 这里 +1,因为上面是 <= 符号
left = mid + 1
# 否则右边有序
else:
# 注意:这里必须是 mid+1,因为根据我们的比较方式,mid属于左边的序列
if nums[mid+1] <= target <= nums[right]:
left = mid + 1
else:
# 如果不在右边有序列表中,则在左边无序列表中查找,因此更新右指针
right = mid
# 最后只剩一个元素时,判断是否是目标值,是则返回下标,否则返回-1
return left if nums[left] == target else -1
topic35:搜索插入位置
题目描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
思路:典型的二分搜索思想,分为左右两部分,不在左边就到右边找,知道左右指针指向相同元素,若与所指元素相等,则返回其索引,否则返回-1
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2 # 计算中间位置
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1 # 目标值在右半部分,更新左边界
else:
right = mid - 1 # 目标值在左半部分,更新右边界
return left # 退出循环时,left为插入位置