Open azl397985856 opened 2 years ago
思路: 12月2号刚刚写出来过,一个月后又忘记怎么写了。 【滑动窗口or双指针还是学的不够好】 本题和76题类似。76题是找出最小覆盖子串,30题是找出串联所有单词的子串。 区别:76因为是覆盖所以是滑动窗口,30因为是串联所以是固定窗口。 相同点: 76涉及的是两个字符串 30涉及的是字符串 + 字符串数组 =》本质是两个字符串匹配问题 因此我们可以通过哈希表记录出现的次数和种类: 对于30的话就是单词-》次数,对于76就是 字符-》次数 每当遇到哈希表不存在的值,则allcount++
不同点就是处理方式: 针对30: 首先有不同的起点,比如0从width。每次移动固定的窗口值width。不断右移扩大窗口,用切片保存字符串,判断该字符串是否在哈希表里面,有的话就++,进一步判断是否与哈希表的个数相同,有的话count++。当窗口大于固定窗口时,左移指针。取左边的切片,判断是否存在,存在则--;判断个数是否不同,不同则--。 每次for循环都判断个数是否相同且窗口是否相等,是的话则入输出数组。 针对76: 先右移,判断哈希表是否相同,以及个数是否相同。直到找到满足条件的第一个右窗口,然后从左往右缩直到不符合要求后,继续右移。每次for循环判断是否小于该值,是的话更新输出值。
func findSubstring(s string, words []string) []int {
wordCount,wordL := len(words),len(words[0])
out := []int{}
allcount := 0
find := map[string]int{}
for _,x :=range words{
if find[x] == 0{
allcount++
}
find[x]++
}
for i:=0;i<wordL;i++{
count := 0
have := map[string]int{}
left,right :=i,i+wordL
for ;right<=len(s);right+=wordL{
str := s[right-wordL:right]
if _,ok := find[str];ok{
have[str]++
if have[str] == find[str]{
count++
}
}
for right-left > wordL*wordCount{
str := s[left:left+wordL]
if _,ok := have[str];ok{
have[str]--
}
if have[str] == find[str]-1{
count--
}
left += wordL
}
if count == allcount && right-left == wordL*wordCount{
out = append(out,left)
}
}
}
return out
}
时间复杂度O(N) 空间复杂度O(N)
暴力法,遍历每个word,判断符合条件的,不符合直接结束当前检查
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res=new ArrayList<>();
if (words==null||words.length==0)return res;
int wordNum = words.length,wordLen=words[0].length();
HashMap<String,Integer> allWords=new HashMap<>();
for (String word : words) {
Integer value = allWords.getOrDefault(word, 0);
allWords.put(word,++value);
}
for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {
HashMap<String,Integer> hasWords=new HashMap<>();
int count=0;
while (count<wordNum){
String word = s.substring(i + count * wordLen, i + (count + 1) * wordLen);
if (allWords.containsKey(word)){
Integer value = hasWords.getOrDefault(word, 0);
hasWords.put(word,++value);
if (hasWords.get(word)>allWords.get(word))break;
}else {
break;
}
count++;
}
if (count==wordNum)res.add(i);
}
return res;
}
}
时间复杂度 O(n*m) 空间复杂度 O(n)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
if (words == null || words.length == 0)
return res;
for (String word : words)
map.put(word, map.getOrDefault(word, 0) + 1);
int sLen = s.length(), wordLen = words[0].length(), count = words.length;
int match = 0;
for (int i = 0; i < sLen - wordLen * count + 1; i++) {
//得到当前窗口字符串
String cur = s.substring(i, i + wordLen * count);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for (; j < cur.length(); j += wordLen) {
String word = cur.substring(j, j + wordLen);
// 剪枝
if (!map.containsKey(word))
break;
temp.put(word, temp.getOrDefault(word, 0) + 1);
// 剪枝
if (temp.get(word) > map.get(word))
break;
}
if (j == cur.length())
res.add(i);
}
return res;
}
}
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function (s, words) {
//遍历words,将单词和出现的次数存入map中
let map1 = {};
let ans = []
for (let word of words) {
if (map1[word] === undefined) {
map1[word] = 1;
} else {
map1[word] += 1;
}
}
let length = words.join('').length;
for (let i = 0; i < s.length - length + 1; i++) {
let subStr = s.slice(i, i + length);
let map2 = {};
let subStrWords = [];
for (let j = 0; j < words.length; j++) {
let subStrWord = subStr.slice(j * words[0].length, (j + 1) * words[0].length);
if (map1[subStrWord] === undefined) {
break;
} else {
subStrWords.push(subStrWord);
if (map2[subStrWord] === undefined) {
map2[subStrWord] = 1;
} else {
map2[subStrWord] += 1;
}
}
}
if (subStrWords.length === words.length) {
let flag = true;
for (let k = 0; k < subStrWords.length; k++) {
if (map1[subStrWords[k]] !== map2[subStrWords[k]]) {
flag = false;
break;
}
}
if (flag) {
ans.push(i)
}
}
}
return ans;
};
复杂度分析不是很会,不一定对,如果有错,请指正。
思路 滑动窗口,遍历i从 0 到 (s长度-子串长度) 来检测第一个单词(从i位置开始,前x个字母的)是否为words中的单词,如果否直接跳过,开始检测下一个窗口 如果是,则将一个子串长度的切片(x*m个字母)按顺序拆成m个单词,使用Counter进行对比,验证该子串是否为words中的组合
代码
import collections
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
words_dict = collections.Counter(words)
lenw = len(words[0])
len_substr = lenw * len(words)
res = []
if len_substr > len(s):
return []
for i in range(len(s) - len_substr + 1):
if s[i:i+lenw] in words:
checking = collections.Counter([s[j:j+lenw] for j in range(i,i+len_substr,lenw)])
if checking == words_dict:
res.append(i)
return res
复杂度 n为s长度,m为单词个数 时间 最坏O(n*mlogm) 遍历n-m*x次Counter,Counter用时mlogm 空间 O(m) m个单词的Counter
Java Code:
public class Solution {
public ArrayList<Integer> findSubstring(String S, String[] L) {
ArrayList<Integer> result = new ArrayList<Integer>();
HashMap<String, Integer> toFind = new HashMap<String, Integer>();
HashMap<String, Integer> found = new HashMap<String, Integer>();
int m = L.length, n = L[0].length();
for (int i = 0; i < m; i ++){
if (!toFind.containsKey(L[i])){
toFind.put(L[i], 1);
}
else{
toFind.put(L[i], toFind.get(L[i]) + 1);
}
}
for (int i = 0; i <= S.length() - n * m; i ++){
found.clear();
int j;
for (j = 0; j < m; j ++){
int k = i + j * n;
String stub = S.substring(k, k + n);
if (!toFind.containsKey(stub)) break;
if(!found.containsKey(stub)){
found.put(stub, 1);
}
else{
found.put(stub, found.get(stub) + 1);
}
if (found.get(stub) > toFind.get(stub)) break;
}
if (j == m) result.add(i);
}
return result;
}
}
思路
from collections import Counter
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
word_len = len(words[0])
word_cnt = len(words)
words = Counter(words)
res = []
for i in range(word_len):
cur_cnt = 0
l, r = i, i
cur_Counter = Counter()
while r + word_len <= len(s):
w = s[r:r+word_len]
r += word_len
cur_Counter[w] += 1
cur_cnt += 1
while cur_Counter[w] > words[w]:
l_w = s[l:l+word_len]
l += word_len
cur_Counter[l_w] -= 1
cur_cnt -= 1
if cur_cnt == word_cnt:
res.append(l)
return res
*复杂度分析*
- 时间复杂度:O(N)
- 空间复杂度:O(N)
对于每一个start进行一次判断,判断是否之后的词的频率和种类都属于words,如果不是则结束这次判断。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
words_dict = collections.Counter(words)
w_len = len(words[0])
w_num = len(words)
ans = []
for start in range(len(s)-w_len*w_num+1):
curr_dict = dict(words_dict)
nums = 0
for j in range(start,start+w_num*w_len,w_len):
curr_word = s[j:j+w_len]
if curr_word in curr_dict:
curr_dict[curr_word]-=1
nums+=1
if curr_dict[curr_word] == 0:
curr_dict.pop(curr_word)
else:
break
if nums == w_num:
ans.append(start)
return ans
复杂度分析
from collections import Counter
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words: return []
wordMap = Counter(words)
result = []
wordSize, numOfWords = len(words[0]), len(words)
listSize = wordSize * numOfWords
for i in range(len(s)):
seen = dict(wordMap)
wordUsed = 0
for j in range(i, i + listSize, wordSize):
subStr = s[j : j + wordSize]
if subStr in seen and seen[subStr] > 0:
seen[subStr] -= 1
wordUsed += 1
else: break
if wordUsed == numOfWords: result.append(i)
return result
Time Complexity: O(N * L) (L: length of words), Space Complexity: O(N)
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
size = len(words)
window = len(words[0])
helper = {}
res = []
for word in words:
if word not in helper:
helper[word] = 1
else:
helper[word] += 1
for i in range(0, len(s) - size * window + 1):
count = {}
for j in range(i, i + size * window, window):
if s[j: j + window] not in count:
count[s[j: j + window]] = 1
else:
count[s[j: j + window]] += 1
if count == helper:
res.append(i)
return res
复杂度分析
class Solution { public List
Day23 30
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans;
int len_word = words[0].size(),len_words = words.size();
int n = s.size();
if(len_word > n) return ans;
unordered_map<string, int> mp,tmp;
for(int i = 0; i < len_words; ++i){
mp[words[i]]++;
}
for(int i = 0; i <= n - len_word; ++i){
string word = s.substr(i, len_word);
if(mp.find(word)!=mp.end()){
tmp = mp;
int j = i;
for(int z = 0; z < len_words; ++z){
string a = s.substr(j, len_word);
if(tmp.find(a) != tmp.end()&&tmp[a]!=0){
j += len_word;
tmp[a]--;
}
if(j == i + len_word*len_words) ans.push_back(i);
}
}
}
return ans;
}
};
Complexity
O(n^2)
O(n)
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
len_s = len(s)
num_words = len(words)
len_word = len(words[0])
window_size = num_words * len_word
pattern = Counter(words)
words = set(words)
ans = []
for i in range(len_s - window_size + 1):
counter = Counter()
for j in range(i, i + window_size, len_word):
part = s[j: j + len_word]
if part not in words:
break
counter[part] += 1
if counter[part] > pattern[part]:
break
if counter == pattern:
ans.append(i)
return ans
class Solution {
public List
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
if(s == null || words == null) return null;
final int n = words.length;
final int m = words[0].length();
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for(String str : words) {
map.put(str, map.getOrDefault(str, 0) + 1);
}
for(int i = 0; i <= s.length() - m * n; i++) {
Map<String, Integer> copy = new HashMap<>(map);
int k = n;
int j = i;
while(k > 0) {
String str = s.substring(j, j + m);
if(!copy.containsKey(str) || copy.get(str) < 1) break;
copy.put(str, copy.get(str) - 1);
k--;
j += m;
}
if(k == 0) res.add(i);
}
return res;
}
}
复杂度分析
Sliding window. Check each sliding window's substring whether has same word count as the words list
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
def strCount(s, n):
count = {}
for i in range(0, len(s), n):
count[s[i:i+n]] = count.get(s[i:i+n], 0)+1
return count
l = len(words)
lWord = len(words[0])
l *=lWord
count=collections.Counter(words)
i = 0
ans = []
curCount = {}
while i + l<=len(s):
if strCount(s[i:i+l], lWord) == count:
ans.append(i)
i += 1
return ans
Time: O(N^2) Space: O(N)
设n为字符串长度,m为words个数,k为单个单词的长度
naive思路:缝缝补补拼凑而成,最后成功但TLE的方案;其实正确思路就在这里面了,就不考虑复杂度了(很乱)
遍历+哈希:符合要求的子串长度一定是len(words[0])*len(words)
,直接遍历s,取出这些子串,再对这些子串判断其是否符合要求即可。(对words建哈希统计,看子串能否符合要求即可)。有n个子串,每个子串要在哈希表中访问m次来确定子串是否符合要求,复杂度为$O(n*m)$
遍历+哈希+条件优化:上述方法的改进,不再一个字符一个字符的跳,而是一个单词一个单词的跳跃,且根据不同情况优化跳跃,以达到剪枝的效果^问题 。
i
跳到这个词后面,j
从i
开始,当前哈希表重置i
,将i
向后跳一个单词的长度,同时调整当前哈希表(删除跳过的单词),j
从当前位置继续i
开始向右跳并调整当前哈希表(删掉跳过的词),直到重复次数等于当前结果。j
从当前位置继续其实这个方法依然有n个子串要考虑,但是由于对哈希表的维护已经不同的跳跃规则,所以每个子串中对哈希表的访问变少了。具体的:
平均每个子串只访问了一次哈希表,所以复杂度为$O(n)$
class Solution:
# naive思路:缝缝补补拼凑而成,最后成功但TLE的方案;其实正确思路就在这里面了
def findSubstring1(self, s: str, words: List[str]) -> List[int]:
length = len(words[0])
words_ans = collections.Counter(words)
ans = set()
for x in range(len(s) - length * len(words) + 1):
i, j, n = 0 + x, 0 + x, len(s)
words_cnt = collections.defaultdict(int)
while j < n:
if (word := s[j:j + length]) in words_ans:
words_cnt[word] += 1
j += length
while words_cnt[word] > words_ans[word]:
if s[i:i + length] in words_cnt:
words_cnt[s[i:i + length]] -= 1
i += length
else:
i += 1
else:
words_cnt = collections.defaultdict(int)
j += 1
i = j
if words_cnt == words_ans:
ans.add(i)
return list(ans)
# 遍历+哈希:符合要求的子串长度一定是(words[0])*len(words),直接遍历s,取出这些子串,
# 再对这些子串判断其是否符合要求即可。(对words建哈希统计,看子串能否符合要求即可)
def findSubstring2(self, s: str, words: List[str]) -> List[int]:
length = len(words[0])
n = length * len(words)
words_ans = collections.Counter(words)
ans = []
for i in range(len(s) - n + 1):
words_cnt = words_ans.copy()
s_clip = s[i:i + n]
j = 0
is_clip = True
while j < n:
if (word := s_clip[j:j + length]) in words_cnt:
if words_cnt[word] == 1:
words_cnt.pop(word)
else:
words_cnt[word] -= 1
j += length
else:
is_clip = False
break
if is_clip and not words_cnt:
ans.append(i)
return ans
# 遍历+哈希+条件优化:上述方法的改进,不再一个字符一个字符的跳,而是一个单词一个单词的跳跃,且根据不同情况优化跳跃,以达到剪枝的效果,
# 因为是一个词一个词跳的,所以整个过程还需要重复单词长度遍(即`len(words[0])`),让`i`分别从不同的起点开始。
# - 找到了不在哈希表里的词:直接将`i`跳到这个词后面,`j`从`i`开始,当前哈希表重置
# - 完全匹配的情况:记录`i`,将`i`向后跳一个单词的长度,同时调整当前哈希表(删除跳过的单词),`j`从当前位置继续
# - 找到的词重复次数过多的情况:让`i`开始向右跳并调整当前哈希表(删掉跳过的词),直到重复次数等于当前结果。`j`从当前位置继续
def findSubstring(self, s: str, words: List[str]) -> List[int]:
length = len(words[0])
n = length * len(words)
words_ans = collections.Counter(words)
ans = []
for x in range(length):
i, j = 0 + x, 0
words_cnt = collections.defaultdict(int)
while i <= len(s) - n:
s_clip = s[i:i + n]
is_clip = True
while j < n:
if (word := s_clip[j:j + length]) in words_ans:
words_cnt[word] += 1
j += length
if words_cnt[word] > words_ans[word]:
j_pre = j + i
j = 0
while words_cnt[word] > words_ans[word]:
words_cnt[s_clip[j:j + length]] -= 1
j += length
i += j
j = j_pre - i
is_clip = False
break
else:
is_clip = False
i += j + length
j = 0
words_cnt = collections.defaultdict(int)
break
if is_clip and words_cnt == words_ans:
ans.append(i)
words_cnt[s_clip[:length]] -= 1
i += length
j -= length
return ans
class Solution: def findSubstring(self, s: str, words: List[str]) -> List[int]: from itertools import permutations length = len(words) com = list(set(permutations(words,length))) strs = [] for i in com: strs.append(''.join(i)) ans = [] for i in range(0,len(s)-len(strs[0])+1): if s[i:i+len(strs[0])] in strs: ans.append(i) return ans
滑动窗口 确定字符串长度和单词个数,将words存入map中。 遍历字符串中前k个字符(就相当于以这些字符作为开头字符去找单词) 定义左右两个指针 定义一个哈希表,和count计数 哈希表存储左右指针当中的存在于map中的单词,count是他们的数量 右指针始终指向最新单词的首个字符,以s[r:r+k]来取到最新的单词word 左指针的任务 1.当word存在于map中时,计数器中的单词数目超过map中的数目,则左移指针直到符合map数目要求 2.不存在时就一直遍历到r指针的位置,清理之前的计数。 遍历过程中如果count等于len(words) res = append(res,l)
func findSubstring(s string, words []string) []int {
if len(words) < 1 {return []int{}}
slen := len(s)
wlen := len(words)
k := len(words[0])
if slen < k*wlen{ return []int{}}
var res = make([]int,0)
var hashmap = make(map[string]int)
// 将words存入map中
for _,w:= range words{
hashmap[w]++
}
for i:=0; i<k; i++{
var count int = 0
var countermp = make(map[string]int)
for l,r:=i,i; r<=slen-k; r=r+k{
word := s[r:r+k]
if num, found := hashmap[word]; found{
//左指针的任务是当计数器中的单词数目超过map中的数目,则左移指针直到符合map数目要求
for countermp[word] >= num{
countermp[s[l:l+k]]--
count--
l+=k
}
countermp[word]++
count++
}else {
// 如果当前单词不在词典里,左移指针至下一个单词,左移过程中清理计数
for l < r {
countermp[s[l:l+k]]--
count--
l+=k
}
l+=k
}
if count == wlen{
res = append(res, l)
}
}
}
return res
}
时间:O(n2) 空间:O(n)
思路: 从0 - oneWordL每个索引位置开始滑,这样每次滑oneWordL长度,而不是一个字符。 用AllCount去记录words中不同单词的个数,在窗口滑动过程中维护Count变量,避免每次扫描两个map判断窗口是否满足条件
func findSubstring(s string, words []string) []int {
wL := len(words) // words 中单词个数
oneWordL := len(words[0]) // 每个单词长度
hm := make(map[string]int) // 存words中单词的map
Allcount := 0 // 记录不同单词的个数,用于在窗口滑动过程中判断是否满足答案条件
for i:=0; i<wL; i++{
if _ , ok := hm[words[i]]; !ok{
Allcount++
}
hm[words[i]]++
}
ans := []int{}
for i := 0; i < oneWordL; i++ { // 这里参考各位大佬的思路,从0 - oneWordL的各位置开始向后滑
Count := 0 // 和AllCount对应,这里记录滑动窗口中不同单词的个数
tmpHm := map[string]int{} // 滑动窗口中的单词记录map
left , right := i , i + oneWordL
for ; right <= len(s) ; right += oneWordL{
str := s[right - oneWordL : right]
if _ , ok := hm[str]; ok{
tmpHm[str]++
if tmpHm[str] == hm[str]{ // Count++的条件是该单词出现次数在窗口tmpHm中的出现次数和hm次数一样
Count++
}
} // 如果这一步出现了不相关的单词怎么办?不影响,后面在比较Count和AllCount时,还会比较right - left == oneWordL * wL 。
for right - left > oneWordL * wL{
str := s[left : left + oneWordL]
if _ , ok := tmpHm[str] ; ok{
tmpHm[str]--
}
if tmpHm[str] + 1 == hm[str]{ // 这个条件重要,tmpHm[str] < hm[str]不对
Count--
}
left += oneWordL
}
if Count == Allcount && right - left == oneWordL * wL{ // 两个条件判断是否满足答案
ans = append(ans , left)
}
}
}
return ans
}
时间复杂度:n 空间复杂度:n
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
先遍历一遍words,创建一个哈希表,保存单词出现的次数,然后维护一个滑动窗口,长度为子列的长度,遍历这个子列,挨个寻找单词我们哈希表中有没有,如果有的话,次数要减去1,如果没有,或者是count已经为0了就返回false,否则在全部遍历完后返回true
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
int numOfWords = words.length;
int lengthOfSingleWord = words[0].length();
int length = s.length();
//子列应有的总长度
int lengthOfSubString = numOfWords * lengthOfSingleWord;
//如果length比子列应有的总长度小,那么就没有有效答案
if(length < lengthOfSubString){
return res;
}
//先遍历words,创建一个哈希表,在哈希表中记录 单词->出现次数
Map<String,Integer> map = new HashMap<>();
for(int i = 0; i < numOfWords; i++){
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
}
for(int left = 0; left + lengthOfSubString <= length; left ++){
String temp = s.substring(left, left + lengthOfSubString);
Map<String,Integer> tempMap = new HashMap<>();
//这里要用putAll深度克隆哈希表
tempMap.putAll(map);
if(isEqual(temp, numOfWords, lengthOfSingleWord, tempMap)){
res.add(left);
}
}
return res;
}
public boolean isEqual(String temp, int numOfWords, int lengthOfSingleWord, Map<String,Integer> map){
for(int count = 0, left = 0, right = lengthOfSingleWord; count < numOfWords; count ++){
String word = temp.substring(left, right);
if(!map.containsKey(word)){
return false;
}
if(map.get(word) == 0){
return false;
}
//map中有这个key,且对应value不为0
map.put(word, map.get(word) - 1);
left = right;
right += lengthOfSingleWord;
}
return true;
}
}
时间复杂度:$O(n^2)$
空间复杂度:$O(n)$
解题思路 大致思路 数组中元素长度是固定的,通过在s上每次用substr 截取 i 到 元素长度 * 数组长度的 范伟,进行元素长度的分割,和words数组进行比较 是否相同
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
if (!words[0] || s === '' || s.length < words[0].length) return []
const wordsLen = words.length
const wordLen = words[0].length
const matchLen = wordsLen * wordLen
const ids = []
const wordMap = {}
words.forEach((d) => {
if(!wordMap[d]) {
wordMap[d] = 1
} else {
wordMap[d] += 1
}
})
const wordMapLen = Object.keys(wordMap).length
// console.log('--------w',wordMap)
for(let i = 0; i < s.length; i++) {
let str = s.substr(i, matchLen)
if (str.length >= matchLen) {
const arr = []
for(let i = 0; i < str.length / wordLen ; i++) {
arr.push(str.substr(i * wordLen, wordLen))
}
const arrMap = {}
arr.forEach((d) => {
if(arrMap[d]) {
arrMap[d]++
} else {
arrMap[d] = 1
}
})
const keys = Object.keys(arrMap)
if (keys.length === wordMapLen) {
if(
keys.every((k) => {
return (arrMap[k] === wordMap[k])
})
) {
console.log(arr, arrMap, wordMap)
ids.push(i)
}
}
}
}
return ids;
};
思路:
遍历出s所有满足长度所需子串,将子串按照words长度分割,保存至哈希表;
将words的单词保存至哈希表,比较两个哈希表各单词出现次数都相等,则子串满足条件;
步骤:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ans = new ArrayList();
int wordLength = words[0].length();
int wordsLength = wordLength*words.length;
if(s.length()<wordsLength){
return ans;
}
Map<String,Integer> wordsMap = new HashMap();
for(int i = 0;i<words.length;i++){
wordsMap.put(words[i],wordsMap.getOrDefault(words[i],0)+1);
}
int left = 0,right = wordsLength;
while(right<=s.length()){
Map<String,Integer> map = new HashMap();
boolean flag = true;
String sub = s.substring(left,right);
for(int i = 0;i<sub.length();i+=wordLength){
String substring = sub.substring(i,i+wordLength);
map.put(substring,map.getOrDefault(substring,0)+1);
}
for(Map.Entry<String,Integer> entry : map.entrySet()){
String mapKey = entry.getKey();
int count = (Integer)entry.getValue();
if(!(count==(Integer)wordsMap.getOrDefault(mapKey,0))){
flag = false;
}
}
if(flag == true){
ans.add(left);
}
left++;
right++;
}
return ans;
}
}
时间:O((n-m+1)*i),n为字符串s长度,m为words所有字符数,i为words数组长度;
空间:O(i),为words数组长度;
code:
public List<Integer> findSubstring(String s, String[] words) {
if (s == null || words == null || words.length == 0) {
return new ArrayList<>();
}
List<Integer> res = new ArrayList<>();
int n = words.length;
int m = words[0].length();
HashMap<String, Integer> map = new HashMap<>();
for (String str : words) {
map.put(str, map.getOrDefault(str, 0) + 1);
}
for (int i = 0; i <= s.length() - n * m; i++) {
HashMap<String, Integer> copy = new HashMap<>(map);
int k = n;
int j = i;
while (k > 0) {
String str = s.substring(j, j + m);
if (!copy.containsKey(str) || copy.get(str) < 1) {
break;
}
copy.put(str, copy.get(str) - 1);
k--;
j += m;
}
if (k == 0) res.add(i);
}
return res;
}
Use two hashmaps to check whether the string is concatenated in a correct way.
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
// count each word in words
Map<String, Integer> counter = new HashMap<>();
for (String word : words) {
counter.put(word, counter.getOrDefault(word, 0) + 1);
}
List<Integer> indexes = new ArrayList<>();
int n = s.length(); // the length of string s
int num = words.length; // number of words
int len = words[0].length(); // len of each word
for (int i = 0; i < n - num * len + 1; i++) {
Map<String, Integer> seen = new HashMap<>();
int j = 0;
while (j < num) {
String word = s.substring(i + j * len, i + (j + 1) * len); //jump by len of each word
if (counter.containsKey(word)) {
seen.put(word, seen.getOrDefault(word, 0) + 1);
if (seen.get(word) > counter.getOrDefault(word, 0)) {
break;
}
} else {
break;
}
j++;
}
if (j == num) {
indexes.add(i);
}
}
return indexes;
}
}
复杂度分析
滑动窗口 + 哈希表
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> ori = new HashMap<>();
for (String word : words) {
ori.put(word, ori.getOrDefault(word, 0) + 1);
}
List<Integer> ans = new ArrayList<>();
int step = words[0].length();
for (int i = 0; i < step; ++i) {
int l = i, r = i;
Map<String, Integer> cur = new HashMap<>();
while (r + step <= s.length()) {
String next = s.substring(r, r + step);
r += step;
if (ori.containsKey(next)) {
cur.put(next, cur.getOrDefault(next, 0) + 1);
while (cur.get(next) > ori.get(next)) {
String tmp = s.substring(l, l + step);
l += step;
cur.put(tmp, cur.get(tmp) - 1);
}
boolean flag = true;
for (String ss : ori.keySet()) {
if (!ori.get(ss).equals(cur.get(ss))) {
flag = false;
}
}
if (flag) {
ans.add(l);
}
} else {
l = r;
cur.clear();
}
}
}
return ans;
}
}
# 2 maps
# build counter of word in words
# iterate through s and check every possible window of length total words length
# break if a new word appears in the substring or counter exceeds
# time: O(len(s) * words_length * num_of words)
# space: O(number of unique words)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
words_length = len(words) * len(words[0])
# not enough chars in s
if words_length > len(s):
return []
counter = {}
res = []
# build counter
for word in words:
counter[word] = counter.get(word, 0) + 1
for i in range(len(s) - words_length + 1):
seen = {}
# increment by single word length
for j in range(i, i + words_length, len(words[0])):
next_word = s[j : j + len(words[0])]
if next_word in counter:
seen[next_word] = seen.get(next_word, 0) + 1
if seen[next_word] > counter[next_word]:
break
else:
break
if seen == counter:
res.append(i)
return res
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
if(s == null || words == null || words.length == 0) return new ArrayList<>();
List<Integer> res = new ArrayList<>();
int m = words.length, n = words[0].length();
HashMap<String, Integer> map = new HashMap<>();
for(String str:words) {
map.put(str, map.getOrDefault(str, 0)+1);
}
for(int i = 0; i <= s.length() - m*n; i++) {
HashMap<String, Integer> copy = new HashMap<>(map);
int p = m;
int q = i;
while(p > 0) {
String temp = s.substring(q, q+n);
if(!copy.containsKey(temp) || copy.get(temp) < 1) break;
copy.put(temp, copy.get(temp)-1);
p--;
q += n;
}
if(p==0) res.add(i);
}
return res;
}
}
哈希表+ 滑动窗口
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function (s, words) {
if (words === null || words.length === 0) {
return [];
}
const res = [];
const map = new Map();
// 哈希存储单词出现次数
for (const word of words) {
map.set(word, (map.get(word) || 0) + 1);
}
// 字符串长度,单个单词长度,单词总长度
const sLen = s.length, wordLen = words[0].length, wholeLen = words.length * words[0].length;
for (let i = 0; i <= sLen - wholeLen; i++) {
// 求出当前窗口内容
const elem = s.substr(i, wholeLen);
// 标记是否成功
let flag = true;
const tempMap = new Map();
for (let j = 0; j < wholeLen; j += wordLen) {
const word = elem.substr(j, wordLen);
// 剪枝,若根本就不存在该单词,直接退出
if (!map.has(word)) {
flag = false;
break;
}
tempMap.set(word, (tempMap.get(word) || 0) + 1);
// 剪枝,若单词出现次数大于所需值,直接退出
if (tempMap.get(word) > map.get(word)) {
flag = false;
break;
}
}
if (flag) {
res.push(i);
}
}
return res;
};
时间:O((sLen - wholeLen) * words.length) 空间:O(wholeLen)
滑动窗口
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
allWords = collections.Counter(words)
wordNum = len(words)
wordLen = len(words[0])
res = []
for i in range(len(s) - wordNum * wordLen + 1):
subWords = collections.defaultdict(int)
index = i
while index < i + wordNum * wordLen:
curWord = s[index: index + wordLen]
if curWord not in allWords or subWords[curWord] == allWords[curWord]:
break
subWords[curWord] += 1
index += wordLen
if index == i + wordNum * wordLen:
res.append(i)
return res
C++ Code:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int word_size = words[0].size();
int word_num = words.size();
if(s.size() < word_size * word_num)
return {};
unordered_map<string, int> record;
for(int i=0; i < words.size(); i++)
record[words[i]]++;
int left =0;
int right = word_size*word_num;
vector<int> ret;
while(right <=s.size())
{
if(isConcat(s, left, right, record, word_size))
{
ret.push_back(left);
}
left++;
right++;
}
return ret;
}
bool isConcat(string& s, int left, int right, unordered_map<string, int>& record, int word_size)
{
unordered_map<string, int> srecord;
while(left < right)
{
string tmp = s.substr(left, word_size);
if(record.count(tmp)==0)
return false;
srecord[tmp]++;
if(srecord[tmp]>record[tmp])
return false;
left += word_size;
}
return true;
}
};
滑动窗口遍历整个字符串,然后再挨个比较String,需要注意的是,由于可能存在重复字符串,因此不能用Set来存储
public List<Integer> findSubstring(String s, String[] words) {
int wordLength = words[0].length();
List<Integer> result = new ArrayList<>();
Map<String, Integer> wordsRecord = new HashMap<>();
for(String word:words) {
wordsRecord.put(word,wordsRecord.getOrDefault(word,0)+1);
}
for(int i = 0; i < s.length();i++) {
Map<String, Integer> temp = new HashMap<>(wordsRecord);
int count = 0;
for(int j = i; j <i+wordLength*words.length && j <= s.length()-wordLength; j+=wordLength) {
String s1 = s.substring(j, j+wordLength);
if((temp.get(s1) == null)||temp.get(s1) <= 0) {
break;
} else {
temp.put(s1, temp.get(s1)-1);
}
count++;
}
temp = null;
if(count == words.length)
result.add(i);
}
return result;
}
时间复杂度:O(M*N) 空间复杂度:O(M)
滑动窗口,窗口内用哈希表依次检查字符串是否包含所有单词表里的单词
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> wordCounters = new HashMap<>();
for (String word: words) {
wordCounters.put(word, wordCounters.getOrDefault(word, 0) + 1);
}
List<Integer> ans = new ArrayList<>();
int wordLen = words[0].length();
int totalLen = words.length * wordLen;
for (int i=0; i + totalLen <= s.length(); i++) {
// String subStr = s.substring(i, i + totaLen);
Map<String, Integer> window = new HashMap<>(wordCounters);
for (int j = i; j < i + totalLen; j += wordLen) {
String word = s.substring(j, j + wordLen);
if (!window.containsKey(word)) {
// not match
break;
} else {
// word count - 1
int count = window.get(word);
if (count == 1) {
window.remove(word);
} else {
window.put(word, count - 1);
}
}
}
if (window.isEmpty()) {
ans.add(i);
}
}
return ans;
}
}
TC: O((n-mk) m k) n = s.length(), m = words.length, k = words[0].length SC: O(m * k)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
from collections import Counter
from collections import defaultdict
word_len = len(words[0])
number_word = len(words)
n = len(s)
if n < word_len:
return []
words = Counter(words)
res = []
for i in range(0, word_len):
left = right = i
cur_count = defaultdict(int)
cnt = 0
while right + word_len <= n:
w = s[right:right+word_len]
right += word_len
if w not in words:
left = right
cur_count.clear()
cnt = 0
else:
cur_count[w] += 1
cnt += 1
while cur_count[w] > words[w]:
left_w = s[left:left+word_len]
left += word_len
cur_count[left_w] -= 1
cnt -= 1
if cnt == number_word:
res.append(left)
return res
time complexity: O(N*k), k stands for the length of a word in words space complexity: O(m), m stands for number of unique words in words
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
if(words.length ==0){
return result;
}
Map<String, Integer> map = new HashMap<>();
for(String word :words){
map.put(word,map.getOrDefault(word,0)+1);
}
Map<String, Integer> found = new HashMap<>();
int n = words.length;
int len = words[0].length();
for(int i=0; i <= s.length() - n * len; i++){
found.clear();
int j=0;
for(j=0; j<n;j++){
String str = s.substring(i+j *len, i + j* len+len);
if(map.containsKey(str)){
found. put(str,found.getOrDefault(str,0)+1);
if(found.get(str)>map.get(str)){
break;
}
}else{
break;
}
}
if (j==n){
result.add(i);
}
}
return result;
}
}
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
const map = {};
words.forEach(item => map[item] = map[item] ? map[item] + 1 : 1);
const n = words[0].length;
const length = words.length*n;
if(s.length<length) return [];
const res = [];
for (let i = 0; i < s.length-length+1; i++) {
contrast(s.slice(i, i+length),map,res,i,n);
}
return res;
};
function contrast(str,map,res,i,n) {
const temp = {...map};
for (let j = 0; j < str.length; j+=n) {
const word = str.slice(j,j+n);
if(!temp[word]) return;
temp[word]--;
if(temp[word]===0) delete temp[word];
}
if(Object.keys(temp).length===0) res.push(i);
}
滑动窗口
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function (s, words) {
if (!s || !words || !words.length) return []
// key是单词,value是出现的次数
const wordsMap = new Map()
for (const word of words) wordsMap.set(word, (wordsMap.get(word) || 0) + 1)
// 一个单词的长度
const firstWordLen = words[0].length
// 所有单词的长度
const allWordsLen = firstWordLen * words.length
if (s.length < allWordsLen) return []
const res = []
// 为什么要小于一个单词的长度,即i < firstWordLen ?
// 因为每次移动一个单词的长度,即3个字符,所以所有的移动被分成了三类 1、从0开始,每次移动一个单词的长度;2、从1开始,每次移动一个单词的长度;3、从2开始,每次移动一个单词的长度
// 每次窗口大小 wordsLen,每次后移一个单词长度,由左右窗口维持当前窗口位置
for (let i = 0; i < firstWordLen; i++) {
let left = i, right = i
// 符合要求的单词数
// 符合要求的word窗口
let count = 0, tmpMap = new Map()
// 右窗口不超出s的长度,每次移动一个单词的长度
while (right + firstWordLen <= s.length) {
const word = s.substring(right, right + firstWordLen)
right += firstWordLen // 右窗口右移
if (wordsMap.has(word)) {
// 统计当前子串中这个单词出现的次数
tmpMap.set(word, (tmpMap.get(word) || 0) + 1)
count++
// 一直减,减到不大于,即重复单词前面的都是没用的,左窗口直接跳过
while (tmpMap.get(word) > wordsMap.get(word)) {
let tmpWord = s.substring(left, left + firstWordLen)
count--
tmpMap.set(tmpWord, (tmpMap.get(tmpWord) || 0) - 1)
// 左窗口右移动,即缩小
left += firstWordLen
}
// 当前窗口字符串个数满足要求,此时窗口的单词刚好满足words
if (count == words.length) res.push(left)
} else {
// words中没有这个单词,则左指针移动,缩小窗口,直接右移到这个单词后面
// 左指针直接移动到右窗口的位置,包含该不符合字符的串都直接跳过
left = right
// 窗口内单词统计map清空,重新统计
tmpMap.clear()
// 符合要求的单词数清0
count = 0
}
}
}
// 返回能拆分成words的所有起始索引
return res
};
时间复杂度 O(n * m)
空间复杂度 O(n)
//滑动窗口抄代码: Space 为O((s.length() - words.length * words[0].length()) * words.length * words[0].length()) = O(M*N*K);Time为O(words.length) = O(M)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList();
int wordlen = words[0].length();
int len = wordlen * words.length;
Map<String, Integer> map = new HashMap();
for(String word : words){
map.put(word, map.getOrDefault(word, 0) + 1);
}
for(int i = 0; i < s.length() - len + 1; i++){
String cur = s.substring(i, i + len);
Map<String, Integer> tmp = new HashMap();
int j = 0; //外在指标,不用新建指标,又方便又轻松。
for(; j < len; j += wordlen){
String word = cur.substring(j , j + wordlen);
if(!map.containsKey(word)){
break;
}
tmp.put(word, tmp.getOrDefault(word, 0) + 1);
if(tmp.get(word) > map.get(word)){ //不用1是因为有的words存在多个数,题目要求每一个元素仅出现一次
break;
}
}
if(j == len) {
res.add(i);
}
}
return res;
}
}
滑动窗口+哈希表
先构建一个wordsHash对words中的单词进行统计
利用words总字符数为长度的滑动窗口对s进行截取,再截取words单个单词长度的字符串计入matchHash(当然该字符串必须在wordsHash内)
最后比较wordsHash和matchHash是否相等
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int onelen = words[0].length();
int alllen = onelen*words.size();
vector<int> res;
unordered_map<string,int> wordsHash;
for(int i = 0;i<words.size();i++) wordsHash[words[i]]++; //
for(int i = 0;i+alllen<=s.length();i++){
//j = i+alllen;
string temp = s.substr(i,alllen); //截取需要匹配的字符串
bool flag = true;
unordered_map<string,int> matchHash;
for(int j = 0;j<temp.length();j += onelen){
string cur = temp.substr(j,onelen); //截取单个字符
if(wordsHash.find(cur) != wordsHash.end()) matchHash[cur]++;
else{
//如果cur不在总hash里
flag = false;
break;
}
}
if(!flag || wordsHash.size() != matchHash.size()) continue;
bool flag1 = true;
//最后的匹配
for(auto it = matchHash.begin();it!=matchHash.end();it++){
if(wordsHash[it->first] != matchHash[it->first]){
flag1 = false;
break;
}
}
if(flag1) res.push_back(i);
}
return res;
}
};
复杂度分析
时间复杂度:O(n m k) n为s的长度,m为单个单词的长度,k为words的长度(即单词的个数)
空间复杂度:O(k)
# 思路 滑动窗口+哈希表
# allWords 用于记录words中单词出现的次数,subWords 用于记录子串中(也就是滑动窗口中)单词出现的次数,wordNum 为单词的个数,wordLen为单词长度
# 遍历字符串,移动长度为 wordNum * wordLen 的滑动窗口,再在当前滑动窗口中依次比较wordLen长度的单词
# 当这个窗口内一旦出现不存在allWords中的单词,或者这个单词在子串中出现的次数已经等于allWords中的次数(也就是再加入这个子串次数就要超出了),这个滑动窗口就不符合要求,直接break进入下一个滑动窗口的匹配
# 当完全匹配上时,把滑动窗口的起始索引加入结果res中
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
allWords = collections.Counter(words)
wordNum = len(words)
wordLen = len(words[0])
res = []
for i in range( len(s) - wordNum * wordLen +1 ):
subWords = collections.defaultdict(int)
index = i
while index < i + wordNum * wordLen:
curWord = s[index: index + wordLen]
if curWord not in allWords or subWords[curWord] == allWords[curWord]:
break
subWords[curWord] += 1
index += wordLen
if index == i + wordNum * wordLen:
res.append(i)
return res
参看"今天比昨天厉害“ 的题解
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
# edge
if len(s) == 0 or len(words[0]) == 0:
return []
hashmap = {}
# create hashmap
for word in words:
hashmap[word] = hashmap.get(word, 0) + 1
result = []
word_count = len(words)
word_length = len(words[0])
# set up window
for i in range((len(s) - word_count * word_length) + 1):
hashmap2 = {}
for j in range(0, word_count):
next_word_index = i + j * word_length
word = s[next_word_index : next_word_index + word_length]
if word not in hashmap:
break
hashmap2[word] = hashmap2.get(word, 0) + 1
if hashmap2[word] > hashmap.get(word, 0):
break
if j + 1 == word_count:
result.append(i)
return result
class Solution {
public:
vector
滑动窗口+哈希表 统计words中的词频,以words[0]*len(words)作为窗口大小在s上滑动,统计这个窗口中的词频,与words词频相同,则记录下起始位置
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
word_len = len(words[0])
substr_len = word_len * len(words)
ans = []
if len(s) < substr_len:
return ans
#统计words词频
word_freq = {}
for word in words:
if word in word_freq.keys():
word_freq[word] += 1
else:
word_freq[word] = 1
for i in range(len(s)):
if i + substr_len <= len(s):
substr_freq = {}
j = i
while j < i+substr_len:
word = s[j:j+word_len]
if word not in word_freq.keys():
break
else:
if word in substr_freq.keys():
substr_freq[word] += 1
else:
substr_freq[word] = 1
if substr_freq[word] > word_freq[word]:
break
j += word_len
if j == (i+substr_len):
ans.append(i)
return ans
时间复杂度:O(nmk),n为s长度,m为words词数,k为单个word的长度 空间复杂度:O(m)
哈希表+滑动窗口
class Solution {
/**
* @param String $s
* @param String[] $words
* @return Integer[]
*/
function findSubstring($s, $words) {
for($i=0;$i<count($words);$i++){
if(isset($hashT[$words[$i]])){
$hashT[$words[$i]]++;
}
else{
$hashT[$words[$i]] = 1;
}
}
$res = [];
$len = strlen($words[0]);
$windowsLen = count($words) * $len;
$i=0;
while($i<=strlen($s)-$windowsLen){
$m = $i;
$temp = array_slice($hashT,0);
$flag=0;
$times = count($words);
while($times>0){
$matchStr = substr($s,$m,$len);
if($temp[$matchStr]>=1){
$m+=$len;
$temp[$matchStr]--;
$times--;
}
else{
$flag=1;
break;
}
}
if($flag==0){
$res[] = $i;
}
$i++;
}
return $res;
}
}
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String,Integer> map = new HashMap<>();
for(String str : words){
if(map.containsKey(str)) map.put(str,map.get(str)+1);
else map.put(str,1);
}
int left = 0;
int words_length = words[0].length();
int str_length = words[0].length() * words.length;
List<Integer> ret = new ArrayList<>();
while(left < s.length() - str_length + 1){
String substr = s.substring(left,left + str_length);
HashMap<String,Integer> matchMap = new HashMap<>(map);
for(int i = 0;i<substr.length();i+=words[0].length()){
String tmp = substr.substring(i,i+words[0].length());
if(matchMap.get(tmp) != null) {
matchMap.put(tmp,matchMap.get(tmp) - 1);
if(0 == matchMap.get(tmp)) matchMap.remove(tmp);
}else{
break;
}
}
if(0 == matchMap.size()){
ret.add(left);
}
left++;
}
return ret;
}
}
复杂度 时间 O(N*M) 空间 O(N)
# Sliding window with step len = word size
# Two Dict one for actual dict and the other be temporal
m, n = len(words), len(words[0])
w_count = Counter(words)
res = []
# so start index should in len(word) range to deal with that senario
for idx in range(n):
## each time we should build a count
s_count = Counter()
## loop through s word by word
for i in range(idx, len(s) - n + 1, n):
word = s[i:i + n]
### check if its a valid word
if word in w_count:
s_count[word] += 1
if i >= n * m:
#### find the first word in s_count
first_word = s[(i - n * m): (i - n * m + n)]
if s_count[first_word] == 1:
del s_count[first_word]
else:
#### only if first word is valid should we do -1
#### otherwise s_count[some word] will less than 0
if first_word in w_count:
s_count[first_word] -= 1
if w_count == s_count:
res.append(i - n * (m - 1))
return res
哈希表+滑动窗口
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String,List> d=new HashMap<>();
HashMap<String,Integer>cnt=new HashMap<>();
List<Integer> res=new ArrayList<Integer>();
int m=s.length();
int n=words[0].length();
int k=words.length;
for(String ww:words)
{
d.put(ww,new ArrayList<Integer>());
cnt.put(ww,cnt.getOrDefault(ww,0)+1);
}
for(int k1=0;k1<n;k1++)
{
int right=k1;
int left=k1;
for(String t:d.keySet())
{
d.put(t,new ArrayList<Integer>());
}
while(right<m)
{
right+=n;
if(right>m)
{
break;
}
String w=s.substring(right-n,right);
if (d.get(w)==null)
{
left=right;
for(String t:d.keySet())
{
d.put(t,new ArrayList<Integer>());
}
}
else if(d.get(w).size()>=cnt.get(w))
{
List<Integer> tmp=d.get(w);
left=tmp.get(0)+n;
tmp=tmp.subList(1,tmp.size());
tmp.add(right-n);
d.put(w,tmp);
for (String www:d.keySet())
{
int idx=0;
List<Integer> tmp1=d.get(www);
while(idx<tmp1.size() && tmp1.get(idx)<left)
{
idx+=1;
}
d.put(www,tmp1.subList(idx,tmp1.size()));
}
}
else
{
List<Integer> tmp2=d.get(w);
tmp2.add(right-n);
d.put(w,tmp2);
}
if (right-left==n*k)
{
res.add(left);
}
}
}
return res;
}
}
2 Hash Maps
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for(String w : words){
map.put(w, map.getOrDefault(w, 0) + 1);
}
int wordLen = words[0].length(), wordNum = words.length, sLen = s.length();
int wordsLen = wordLen * wordNum;
int match = 0;
for(int i = 0; i < sLen - wordsLen + 1; i++){
String cur = s.substring(i, i + wordsLen);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for(; j < cur.length(); j += wordLen){
String word = cur.substring(j, j + wordLen);
if(!map.containsKey(word)){
break;
}
temp.put(word, temp.getOrDefault(word, 0) + 1);
if(temp.get(word) > map.get(word)){
break;
}
}
if(j == cur.length()){
res.add(i);
}
}
return res;
}
}
滑动窗口 哈希表
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
HashMap<String, Integer> map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < one_word; i++) {
int left = i, right = i, count = 0;
HashMap<String, Integer> tmp_map = new HashMap<>();
while (right + one_word <= s.length()) {
String w = s.substring(right, right + one_word);
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
right += one_word;
count++;
while (tmp_map.getOrDefault(w, 0) > map.getOrDefault(w, 0)) {
String t_w = s.substring(left, left + one_word);
count--;
tmp_map.put(t_w, tmp_map.getOrDefault(t_w, 0) - 1);
left += one_word;
}
if (count == word_num) res.add(left);
}
}
return res;
}
}
思路: 使用哈希表存放给定的字符串数组,固定宽度的滑动窗口
代码(C++):
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = words.size();
if (n == 0) return {};
int m = words[0].size();
unordered_map<string, int> mw;
for (auto w : words)
mw[w]++;
vector<int> res;
for (int i = 0; i < s.length() - m * n + 1; ++i) {
unordered_map<string, int> ws(mw);
int k = 0;
for (; k < n; ++k) {
string word = s.substr(i + k * m, m);
ws[word]--;
if (ws[word] < 0) break;
}
if (k == n) res.push_back(i);
}
return res;
}
};
30. 串联所有单词的子串
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-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"] 输出:[]