
算法思想(中文解释)
这道题目要求我们在字符串 s 中找到所有子串,这些子串是字符串数组 words 中所有单词的串联,并且每个单词只能使用一次,且顺序可以任意。下面是代码的算法思想:
1. 核心思路
分解问题:
- 因为每个单词长度相同,我们可以使用一个滑动窗口(
Sliding Window)来检查所有可能的子串。 - 判断一个子串是否是所有单词的串联,可以通过比较单词的频次。
2. 步骤讲解
(1)初始化:
- 单词长度:用
wordLength表示words中每个单词的长度(因为题目保证它们长度相同)。 - 总串联长度:
totalLength = wordLength * words.length,因为子串的长度一定是所有单词的长度之和。 - 构造单词频次表:用
wordMap记录words中每个单词的出现次数,便于后续比较。
(2)滑动窗口遍历:
- 从字符串
s的每个位置开始,以长度为totalLength的窗口进行检查:- 提取窗口中的子串
sub。 - 检查这个子串是否包含了
words中所有单词且频次正确。
- 提取窗口中的子串
(3)子串验证:
- 对于窗口中的子串
sub,将其按照wordLength分割成一个个小单词。 - 检查这些小单词是否在
wordMap中,并验证它们的出现频次是否超出限制:- 如果某个单词不在
wordMap中,立即返回false; - 如果某个单词出现的次数超过了在
wordMap中的次数,也返回false。
- 如果某个单词不在
- 如果所有单词验证通过,则说明当前窗口位置是符合要求的,记录下起始索引。
3. 时间复杂度分析
- 构造单词频次表:
O(m),其中m是words的长度(即单词个数)。 - 滑动窗口遍历:外层遍历最多需要
n - totalLength + 1次,n是字符串s的长度。 - 验证子串:每次验证需要遍历窗口中的所有单词,复杂度为
O(m * wordLength)。
因此,总复杂度为:
[ O((n - totalLength + 1) \cdot m \cdot wordLength) ]
通常可以简化为 O(n * m),适用于 s 较长和 words 较短的场景。
4. 代码逻辑解释
主函数:
wordMap:统计words中每个单词的出现频次。- 滑动窗口遍历:通过
for循环,遍历从 0 到s.length() - totalLength的所有可能起始位置。 - 子串验证:调用辅助函数
isValid()检查是否符合要求。
辅助函数 isValid:
- 将子串
sub分割成长度为wordLength的小单词。 - 使用
seen哈希表记录窗口内每个单词的频次。 - 与
wordMap进行比较,判断是否匹配。
5. 关键优化点
- 滑动窗口:避免暴力检查所有子串,只检查可能的窗口,减少不必要的计算。
- 哈希表:使用
wordMap和seen快速判断频次关系,而不是逐一比较。 - 提前退出:在验证过程中,一旦发现不匹配的单词,立即退出验证,避免冗余计算。

最低0.47元/天 解锁文章
699

被折叠的 条评论
为什么被折叠?



