leetcode-pp / 91alg-6-daily-check

91 算法第六期打卡仓库
28 stars 0 forks source link

【Day 23 】2022-01-03 - 30. 串联所有单词的子串 #30

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

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"] 输出:[]

yan0327 commented 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)

gova-i267 commented 2 years ago

思路:

暴力法,遍历每个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)

wbcz commented 2 years ago

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;
}

}

wxj783428795 commented 2 years ago

思路

  1. 将words中单词和出现的次数以key和value存入一个map中。
  2. 遍历s,截取words中所有字符的总长度的子字符串。
  3. 遍历子字符串,去除子字符串中的单词,判断其在不在第一个map中,如果在,就将单词和出现次数存入第二个map中。如果不在,则直接跳过,进入下一次循环。
  4. 比较map1和map2的key,value是否相同,如果相同,则记录当前下标。

代码

/**
 * @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;
};

复杂度分析

复杂度分析不是很会,不一定对,如果有错,请指正。

yetfan commented 2 years ago

思路 滑动窗口,遍历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

wangzehan123 commented 2 years ago

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;
    }
}
feifan-bai commented 2 years ago

思路

  1. 滑动窗口,在s维护一个所有单词长度和的队列
  2. 设置多起点,滑动窗口求解 代码
    
    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)
Bochengwan commented 2 years ago

思路

对于每一个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

复杂度分析

xj-yan commented 2 years ago
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)

zwmanman commented 2 years ago

思路

  1. sliding window: out loop iterate from first to len(s) - # of word * word_length
  2. inside loop iterate from i with window size as step

代码

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

复杂度分析

honeymeng-hub commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { List res = new ArrayList<>(); Map<String, Integer> map = new HashMap<>(); i++f (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; } }

CodeWithIris commented 2 years ago

Question

Day23 30
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

Note (Hash table, Sliding window)

Solution (C++)


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

ZhangNN2018 commented 2 years ago

思路

复杂度

代码

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 
codingowl0101 commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { List 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; } }

RocJeMaintiendrai commented 2 years ago
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;
    }
}

复杂度分析

ZacheryCao commented 2 years ago

Idea

Sliding window. Check each sliding window's substring whether has same word count as the words list

Code

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

Complexity:

Time: O(N^2) Space: O(N)

CoreJa commented 2 years ago

思路

设n为字符串长度,m为words个数,k为单个单词的长度

代码

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
zjsuper commented 2 years ago

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

Toms-BigData commented 2 years ago

【Day 23】30. 串联所有单词的子串

思路

滑动窗口 确定字符串长度和单词个数,将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)

golang代码

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)

hdyhdy commented 2 years ago

思路: 从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

xinhaoyi commented 2 years ago

30. 串联所有单词的子串

给定一个字符串 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)$

kandejiandefeng commented 2 years ago

解题思路 大致思路 数组中元素长度是固定的,通过在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;
};
declan92 commented 2 years ago

思路:
遍历出s所有满足长度所需子串,将子串按照words长度分割,保存至哈希表; 将words的单词保存至哈希表,比较两个哈希表各单词出现次数都相等,则子串满足条件; 步骤:

  1. 左右指针长度间隔words字符数,向右滑动遍历字符串s;
  2. 子串使用words[0].length长度截取,保存至哈希表,key记录单词字符串,value记录单词次数;
  3. words遍历保存至哈希表,比较两个哈希表是否一致,一致则记录左指针索引; java
    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数组长度;

ivangin commented 2 years ago

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;
    }
zwang2244 commented 2 years ago

思路

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;
    }
}

复杂度分析

Moin-Jer commented 2 years ago

思路


滑动窗口 + 哈希表

代码


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;
    }
}

复杂度分析


laofuWF commented 2 years ago
# 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
Husky-Gong commented 2 years ago
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;
    }
}
Flower-F commented 2 years ago

解题思路

哈希表+ 滑动窗口

代码

/**
 * @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)

devosend commented 2 years ago

思路

滑动窗口

代码

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
zhangzz2015 commented 2 years ago

思路

关键点

代码

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; 

    }
};
GaoMinghao commented 2 years ago

思路

滑动窗口遍历整个字符串,然后再挨个比较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)

tongxw commented 2 years ago

思路

滑动窗口,窗口内用哈希表依次检查字符串是否包含所有单词表里的单词

代码

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)

cszys888 commented 2 years ago
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

didiyang4759 commented 2 years ago
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;
    }
}
L-SUI commented 2 years ago
/**
 * @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);
}
HondryTravis commented 2 years ago

思路

滑动窗口

代码

/**
 * @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)

qihang-dai commented 2 years ago
//滑动窗口抄代码: 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;

    }
}
Myleswork commented 2 years ago

思路

滑动窗口+哈希表

先构建一个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)

Alexno1no2 commented 2 years ago
# 思路  滑动窗口+哈希表
# 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
haixiaolu commented 2 years ago

思路: 滑动窗口 + 哈希表

参看"今天比昨天厉害“ 的题解

代码 / Python

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 

复杂度分析

zhangzhengNeu commented 2 years ago

class Solution { public: vector findSubstring(string s, vector& words) { int onelen = words[0].length(); int alllen = onelen*words.size(); vector 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; } };

biscuit279 commented 2 years ago

思路

滑动窗口+哈希表 统计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)

577961141 commented 2 years ago

题目思路

哈希表+滑动窗口

题目的题解code

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;
    }
}

时间和空间复杂度

StoneHIT commented 2 years ago

代码

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)

Leonalhq commented 2 years ago
# 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
BpointA commented 2 years ago

思路

哈希表+滑动窗口

Java代码

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;
    }
}
LannyX commented 2 years ago

思路

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;
    }
}
bigboom666 commented 2 years ago

思路

滑动窗口 哈希表

代码

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;
    }
}
falconruo commented 2 years ago

思路: 使用哈希表存放给定的字符串数组,固定宽度的滑动窗口

代码(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;
    }
};