LeetCode题解:30.串联所有单词的子串【Python题解超详细,KMP搜索、滑动窗口法】,知识拓展:Python中的排列组合

server/2024/12/3 4:06:52/

题目描述

        给定一个字符串 s 和一个字符串数组 words words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd""cdabef", "cdefab""efabcd"和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

        返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 104
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i] 和 s 由小写英文字母组成

解答

class Solution(object):def findSubstring(self, s, words):""":type s: str:type words: List[str]:rtype: List[int]"""# # 思路一:KMP搜索法# def prefix_table(pattern):#     m=len(pattern)#     prefix=[0]*m#     j=0#     for i in range(1,m):#         while j>0 and pattern[i]!=pattern[j]:#             j=prefix[j-1]#         if pattern[i]==pattern[j]:#             j+=1#         prefix[i]=j#     return prefix# def kmp_search(pattern,text):#     m,n=len(pattern),len(text)#     prefix=prefix_table(pattern)#     j=0#     result=[]#     for i in range(n):#         while j>0 and text[i]!=pattern[j]:#             j=prefix[j-1]#         if text[i]==pattern[j]:#             j+=1#         if j==m:#             result.append(i+1-m)#             j=prefix[j-1]#     return result# # 生成所有可能的串联子串# all_patterns=set()# for perm in permutations(words):#     all_patterns.add("".join(perm))# result = []# # 对每个可能的目标串进行 KMP 匹配# for pattern in all_patterns:#     result.extend(kmp_search(pattern,s))# return result# 思路二:if not s or not words:return []# 初始化变量word_len = len(words[0])word_count = len(words)total_len = word_len * word_countwords_freq = {}# 统计 words 中每个单词的频率for word in words:words_freq[word] = words_freq.get(word, 0) + 1result = []# 遍历所有可能的起始点 (最多 word_len 种不同的对齐方式)for i in range(word_len):left = i  # 当前窗口的起始点right = i  # 当前窗口的结束点window_freq = {}count = 0  # 当前窗口内匹配的单词数# 滑动窗口while right + word_len <= len(s):# 取出当前单词word = s[right:right + word_len]right += word_len# 如果是有效单词if word in words_freq:window_freq[word] = window_freq.get(word, 0) + 1# 如果频率不超过要求,增加匹配的单词数if window_freq[word] <= words_freq[word]:count += 1else:# 如果频率超过要求,调整窗口,直到频率合法while window_freq[word] > words_freq[word]:left_word = s[left:left + word_len]window_freq[left_word] -= 1if window_freq[left_word] < words_freq[left_word]:count -= 1left += word_len# 如果窗口内匹配了所有单词,记录起始点if count == word_count:result.append(left)else:# 如果遇到无效单词,直接重置窗口window_freq.clear()count = 0left = rightreturn result          

        思路一,KMP算法:该方法通过生成目标单词的所有排列,并利用 KMP 字符串匹配算法在目标字符串中寻找这些排列的匹配。KMP 算法通过预处理模式字符串,构建前缀函数来加速匹配过程,避免了暴力匹配中的重复计算。尽管 KMP 本身具有线性时间复杂度,但由于需要生成所有可能的单词排列,这使得该方法在多单词组合的情况下计算量大幅增加,导致整体效率不高。

        思路二,滑动窗口法:滑动窗口法通过一个固定大小的窗口在目标字符串中滑动,窗口内的词频统计与目标单词集中的词频进行比较。如果匹配则记录窗口的起始位置,并通过调整窗口来维护频率的合法性。这种方法通过局部更新窗口内容,避免了对整个字符串的重新扫描,通常能够较为高效地处理多个单词的匹配,特别是在目标单词集合较大时具有较低的计算复杂度。

知识拓展: Python中的排列组合

        在 Python 中,排列组合的计算通常可以借助 itertools 库进行实现,或者直接使用 math 库中的一些数学函数来进行相关的数学计算。下面将介绍一些常见的排列组合问题及其在 Python 中的实现方法。

1. 排列(Permutation)

        排列是从一组元素中按照特定顺序选取元素。Python 中没有直接的排列函数,但可以使用 itertools.permutations 来生成排列。

import itertools# 生成从列表 [1, 2, 3] 中选择 2 个元素的所有排列
arr = [1, 2, 3]
perms = itertools.permutations(arr, 2)# 打印所有排列
for perm in perms:print(perm)# 输出为:
# (1, 2)
# (1, 3)
# (2, 1)
# (2, 3)
# (3, 1)
# (3, 2)

        计算排列数:排列数可以通过 math 模块中的 factorial 函数来计算。

import math# 计算从 5 个元素中选取 3 个的排列数
n = 5
k = 3
permutation_count = math.factorial(n) // math.factorial(n - k)
print(permutation_count)# 输出60

2. 组合(Combination)

        组合是从一组元素中选择元素,但不考虑顺序。Python 中可以使用 itertools.combinations 来生成组合。

import itertools# 生成从列表 [1, 2, 3] 中选择 2 个元素的所有组合
arr = [1, 2, 3]
combs = itertools.combinations(arr, 2)# 打印所有组合
for comb in combs:print(comb)# 输出:
# (1, 2)
# (1, 3)
# (2, 3)

        计算组合数:组合数同样可以通过 math 模块中的 factorial 函数来计算。

import math# 计算从 5 个元素中选取 3 个的组合数
n = 5
k = 3
combination_count = math.factorial(n) // (math.factorial(k) * math.factorial(n - k))
print(combination_count)# 输出10

感谢阅读,希望对你有所帮助~


http://www.ppmy.cn/server/146924.html

相关文章

【机器学习】机器学习学习笔记 - 监督学习 - 多项式回归决策树回归 - 03

多项式回归 解决线性回归的准备性不足问题(线性回归只能是直线&#xff0c;多项式回归引入多项式可以是曲线)通过对预测值进行多项式转换, 使得回归模型可以是非线性的多项式回归的优点是可以处理非线性的数据多项式回归的缺点是它对数据进行了多项式转换 加菲工具&#xff0…

Python实现2048小游戏

2048是一个单人益智游戏&#xff0c;目标是移动和合并数字&#xff0c;以达到2048。 1. 实现效果 Python实现2048小游戏 2. 游戏规则 简单地理解一下规则 基本规则&#xff1a; 4x4棋盘&#xff0c;每个格可包含一个2的倍数的数字&#xff0c;初始时为空&#xff0c;表示0。…

Shell脚本小练习

学习了这么长时间Shell脚本&#xff0c;总得来一次小小的练习吧&#xff0c;那么请看下文&#xff01; 1.用Shell写一个小计算器。 通过read命令获取用户输入的表达式&#xff0c;表达式的格式设定为操作数1 运算符 操作数2&#xff0c;例如53&#xff0c;然后利用设计的脚本…

unity中控制相机跟随物体移动

unity中控制相机跟随物体移动 Main Camera下添加组件&#xff08;follow target&#xff09; 脚本中定义 public Transform trans;将transform拖拽到trans中&#xff0c;让trans可以引用到transform数值&#xff08;方式1&#xff09; 因为属于当前GameObject下的脚本组件…

分布式锁的实现原理

作者&#xff1a;来自 vivo 互联网服务器团队- Xu Yaoming 介绍分布式锁的实现原理。 一、分布式锁概述 分布式锁&#xff0c;顾名思义&#xff0c;就是在分布式环境下使用的锁。众所周知&#xff0c;在并发编程中&#xff0c;我们经常需要借助并发控制工具&#xff0c;如 mu…

缓存使用规范学习

1.规范 size控制: string类型&#xff0c;控制在2KB以内 hash、list、set、zset类型的元素个数&#xff0c;不要超过5000 pipeline命令: 检查多参数命令的参数个数或pipeline命令个数&#xff0c;若值太大&#xff0c;建议减小&#xff08;codis proxy返回结果集超64K&…

为什么ai会用python开发

AI领域使用Python开发有几个主要原因&#xff1a; 简洁易读&#xff1a;Python语法简洁&#xff0c;容易理解&#xff0c;使得开发者能够专注于算法和模型的设计&#xff0c;而不是花费大量时间在语言本身的细节上。这对于快速开发和原型设计尤为重要。 强大的库支持&#xff…

Flink四大基石之CheckPoint

1、State Vs Checkpoint State:状态,是Flink中某一个Operator在某一个时刻的状态,如maxBy/sum,注意State存的是历史数据/状态,存在内存中。 Checkpoint:快照点, 是Flink中所有有状态的Operator在某一个时刻的State快照信息/存档信息。 一句话概括: Checkpoint就是State的快照…