菜鸡每日一题系列打卡30天
每天一道算法题目
小伙伴们一起留言打卡
坚持就是胜利,我们一起努力!
题目描述(引自LeetCode)
给定一个字符串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"]
输出:[]
题目分析
这是一道字符串的子串的相关问题,对于这种问题,一种常见的处理方式是滑动窗口。通过两个位置指针所构成的窗口的移动,去判断子串是否符合条件,从而得到答案。详细的逻辑思路讲解见代码注释。
代码实现
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
// 特殊情况判断
if (s == null || s.isEmpty() || words == null || words.length == 0) return result;
Map<String, Integer> map = new HashMap<>();
// 用哈希表存储words数组中的所有word与其出现次数之间的映射
for (String word : words) map.put(word, map.getOrDefault(word, 0) + 1);
// words数组的长度
int allWords = words.length;
// 每个word的长度
int aWord = words[0].length();
for (int i = 0; i < aWord; i++) {
// start与end分别表示窗口的左右边界,flag表示匹配的单词个数
int start = i, end = i, flag = 0;
HashMap<String, Integer> tmp = new HashMap<>();
// 滑动窗口
while (end + aWord <= s.length()) {
// 取单词
String word = s.substring(end, end + aWord);
// 右边界右移
end += aWord;
// 匹配成功,添加至tmp
if (map.containsKey(word)) {
tmp.put(word, tmp.getOrDefault(word, 0) + 1);
flag++;
// 一个单词匹配多次,左边界右移
while (tmp.getOrDefault(word, 0) > map.getOrDefault(word, 0)) {
tmp.put(s.substring(start, start + aWord), tmp.getOrDefault(s.substring(start, start + aWord), 0) - 1);
flag--;
start += aWord;
}
if (flag == allWords) result.add(start);
}
// 匹配失败,重置各项条件
else {
tmp.clear();
flag = 0;
start = end;
}
}
}
// 返回结果
return result;
}
}
代码分析
对代码进行分析,由代码的具体循环逻辑中的访问次数可得,代码的时间复杂度为O(n),而就空间而言,需要用哈希表对word进行存储,因此,在最坏的情况下,代码的空间复杂度为O(n)。
执行结果
写在题目之后
转眼之间,每日一题已经陪伴大家一个月了。
在过去的一个月里,无论忙碌或者清闲,每天都有每日一题如影随形。
最近菜鸡又新开了每日一面栏目,每日系列大家庭更加壮大了。
在这里,菜鸡要特别感谢Bjamekharstar,回忆遗忘,CCC,北辰远等所有小伙伴的陪伴。
是你们的期待,给了菜鸡不断更的信念。
最后,特别感谢我的老婆,你设计的菜鸡形象始终在提醒我——
不忘初心,止于至善。
学习 | 工作 | 分享
????长按关注“有理想的菜鸡”
只有你想不到,没有你学不到