当前位置:网站首页>【综合笔试题】30. 串联所有单词的子串
【综合笔试题】30. 串联所有单词的子串
2022-06-23 11:32:00 【宫水三叶的刷题日记】
题目描述
这是 LeetCode 上的 30. 串联所有单词的子串 ,难度为 困难。
Tag : 「哈希表」、「滑动窗口」、「枚举」
给定一个字符串 s 和一些长度相同的单词 words。
找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
提示:
s由小写英文字母组成words[i]由小写英文字母组成
朴素哈希表
令 n 为字符串 s 的长度,m 为数组 words 的长度(单词的个数),w 为单个单词的长度。
由于 words 里面每个单词长度固定,而我们要找的字符串只能恰好包含所有的单词,因此我们要找的目标子串的长度为 。
那么一个直观的思路是:
使用哈希表 map记录words中每个单词的出现次数枚举 s中的每个字符作为起点,往后取得长度为 的子串sub使用哈希表 cur统计sub每个单词的出现次数(每隔w长度作为一个单词)比较 cur和map是否相同
注意:在步骤 中,如果发现 sub 中包含了 words 没有出现的单词,可以直接剪枝。
剪枝处使用了带标签的 continue 语句直接回到外层循环进行。
代码:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int n = s.length(), m = words.length, w = words[0].length();
Map<String, Integer> map = new HashMap<>();
for (String word : words) map.put(word, map.getOrDefault(word, 0) + 1);
List<Integer> ans = new ArrayList<>();
out:for (int i = 0; i + m * w <= n; i++) {
Map<String, Integer> cur = new HashMap<>();
String sub = s.substring(i, i + m * w);
for (int j = 0; j < sub.length(); j += w) {
String item = sub.substring(j, j + w);
if (!map.containsKey(item)) continue out;
cur.put(item, cur.getOrDefault(item, 0) + 1);
}
if (cur.equals(map)) ans.add(i);
}
return ans;
}
}
时间复杂度:将 words中的单词存入哈希表,复杂度为 (由于字符串长度固定且不超过 ,假定所有哈希操作均为 的);然后第一层循环枚举s中的每个字符作为起点,复杂度为 ;在循环中将sub划分为m个单词进行统计,枚举了m - 1个下标,复杂度为 ;每个字符串的长度为w。整体复杂度为空间复杂度:
滑动窗口 + 哈希表
事实上,我们可以优化这个枚举起点的过程。
我们可以将起点根据 当前下标与单词长度的取余结果 进行分类,这样我们就不用频繁的建立新的哈希表和进行单词统计。
代码:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int n = s.length(), m = words.length, w = words[0].length();
// 统计 words 中「每个目标单词」的出现次数
Map<String, Integer> map = new HashMap<>();
for (String word : words) map.put(word, map.getOrDefault(word, 0) + 1);
List<Integer> ans = new ArrayList<>();
for (int i = 0; i < w; i++) {
// 构建一个当前子串对应 map,统计当前子串中「每个目标单词」的出现次数
Map<String, Integer> curMap = new HashMap<>();
// 滑动窗口的大小固定是 m * w,每次将下一个单词添加进 curMap,上一个单词移出 curMap
for (int j = i; j + w <= n; j += w) {
String cur = s.substring(j, j + w);
curMap.put(cur, curMap.getOrDefault(cur, 0) + 1);
if (j >= i + (m * w)) {
int idx = j - m * w;
String prev = s.substring(idx, idx + w);
if (curMap.get(prev) == 1) curMap.remove(prev);
else curMap.put(prev, curMap.get(prev) - 1);
if (!curMap.getOrDefault(prev, 0).equals(map.getOrDefault(prev, 0))) continue;
}
if (!curMap.getOrDefault(cur, 0).equals(map.getOrDefault(cur, 0))) continue;
// 上面两个 continue 可以减少 map 之间的 equals 操作
if (curMap.equals(map)) ans.add(j - (m - 1) * w);
}
}
return ans;
}
}
时间复杂度:将 words中的单词存入哈希表,复杂度为 (由于字符串长度固定且不超过 ,假定所有哈希操作均为 的);然后枚举了取余的结果,复杂度为 ;每次循环最多处理n长度的字符串,复杂度为 。整体复杂度为空间复杂度:
最后
这是我们「刷穿 LeetCode」系列文章的第 No.30 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
边栏推荐
猜你喜欢

1路百兆光纤收发器1百兆光1百兆电桌面式以太网光纤收发器内置电源

Vone news | wanglian technology empowers the public to enjoy the self-organization management of the chain network, creating an enterprise level alliance Dao

Esp32-cam, esp8266, WiFi, Bluetooth, MCU, hotspot create embedded DNS server

32路电话+2路千兆以太网32路PCM电话光端机支持FXO口FXS语音电话转光纤

【ML】QuantileRegressor

汉源高科1路非压缩4K-DVI光端机4K高清非压缩DVI转光纤4K-DVI高清视频光端机

The simplest DIY actuator controller based on 51 single chip microcomputer

mysql,如何在使用存储过程计算最大值

Design and implementation of esp32-cam wireless monitoring intelligent gateway

使用Mycat进行MySQL单库分表
随机推荐
Necessary software for automation or electrical specialty
KDD 2022 | 基于分层图扩散学习的癫痫波预测
Signature analysis of app x-zse-96 in a Q & a community
网上注册股票开户很困难么?现在网上开户安全么?
Whether Changan Lumin has the ability to become a broken product in the micro electricity market
华为云如何实现实时音视频全球低时延网络架构
Tensorrt笔记(四)推理分割模型
一张图解码 OpenCloudOS 社区开放日
十大劵商如何开户?在线开户安全么?
tensorflow2的GradientTape求梯度
股票网上开户及开户流程怎样?手机开户安全么?
16路HD-SDI光端机多路HD-SDI高清视频光端机16路3G-SDI高清音视频光端机
RF analyzer demo setup
【进程和线程】
Gradienttape of tensorflow2
Vone news | wanglian technology empowers the public to enjoy the self-organization management of the chain network, creating an enterprise level alliance Dao
A child process is created in the program, and then the parent and child processes run independently. The parent process reads lowercase letters on the standard input device and writes them to the pip
Analysis of LinkedList source code
Esp32-cam, esp8266, WiFi, Bluetooth, MCU, hotspot create embedded DNS server
中国十大券商有哪些?手机开户安全么?