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

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 23 】2021-10-02 - 30. 串联所有单词的子串 #39

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

SunStrongChina commented 2 years ago

30. 串联所有单词的子串

入选理由

暂无

题目地址

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

前置知识

  • 哈希表
  • 双指针

题目描述

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]

由于获取words的组合需要O(M!)复杂度,因此尝试使用官方题解第二种解法,找到与len(words)

*len(words[0)的字符串,然后计算是否符合要求。

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        #对于s中每一个和len(words)长度相同的字符串,都比较确认它能否由
        len1=len(words[0])*len(words)

        neededDict={}
        for word in words:
            if word not in neededDict:
                neededDict[word]=1
            else:
                neededDict[word]+=1
        allRes=[]
        for i in range(len(s)-len1+1):
            s1=s[i:i+len1]
            #s1是否可以由words拼凑而成
            #s1等量划分
            parts=[]
            n1=len(s1)//len(words[0])
            for j in range(n1):
                parts.append(s1[j*len(words[0]):(j+1)*len(words[0])])
            #开始比对字符出现次数
            dict1={}
            valid=0
            for part1 in parts:
                if part1 not in dict1:
                    dict1[part1]=1
                else:
                    dict1[part1]+=1
                if part1 in neededDict and dict1[part1]==neededDict[part1]:
                    valid+=1
            if valid==len(neededDict):
                allRes.append(i)

        return  allRes

时间复杂度:O(N*M),N为s的长度,M为words中word单词个数 空间复杂度:O(M)

chang-you commented 2 years ago

Java Code

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

思路

先用全排列,列出words中所有组合。 然后根据长度截断s中的字符,判断是否在string组合里面。 缺点: 该算法时间复杂度较高,后期完善。

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (s.empty())  return res;
        set<string> strs;
        string tmp;
        helper(tmp, 0, words.size() - 1, words, strs);
        for (auto iter = strs.begin(); iter != strs.end(); iter++) {
            int length = iter->length();
            for (int j = 0; j + length <= s.length(); j++) {
                if (s.substr(j, length) == *iter) {
                    res.push_back(j);
                }
            }
        }
        return res;
    }
    void helper(string& s, int start, int end, 
                vector<string>& words, set<string>& strs) {
        if (start >= end) {
            for (auto tmp_s : words) { s += tmp_s; }
            strs.insert(s);
            s.clear();
            return;
        }
        for (int i = start; i <= end; i++) {
            swap_str(words[start], words[i]);
            helper(s, start+1, end, words,  strs);
            swap_str(words[start], words[i]);
        }
        return;
    }
    void swap_str(string& a, string& b) {
        string tmp = a;
        a = b;
        b = tmp;
        return;
    }
};
qyw-wqy commented 2 years ago
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> list = new ArrayList<>();
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < words.length; i++) {
            map.put(words[i], map.getOrDefault(words[i], 0) + 1);
        }
        int wordLen = words[0].length();
        int totalLen = wordLen * words.length;
        for (int i = 0; i <= s.length() - totalLen; i++) {
            Map<String, Integer> temp = new HashMap<>();
            for (int j = 0; j < words.length; j++) {
                String substr = s.substring(i + j * wordLen, i + (j+1) * wordLen);
                if (!map.containsKey(substr)) break;
                temp.put(substr, temp.getOrDefault(substr, 0) + 1);
                if (temp.get(substr) > map.get(substr)) break;
                if (j == words.length-1) list.add(i);
            }
        }
        return list;
    }
}

Time O(NMK). N: length of s, M: size of words array, K: length of word in words Space O(M * K)

itsjacob commented 2 years ago

Intuition

Implementation

class Solution
{
public:
  vector<int> findSubstring(string s, vector<string> &words)
  {
    std::unordered_map<std::string, int> window;
    std::unordered_map<std::string, int> needed;
    for (auto &w : words) {
      if (needed.count(w) == 0) {
        needed.emplace(w, 1);
      } else {
        needed[w]++;
      }
    }

    std::vector<int> res;
    int wsize = words[0].size();
    int wordssize = wsize * words.size();
    for (int ii = 0; ii < wsize; ii++) {
      window.clear();
      int valid{ 0 };
      int left{ ii };
      int right{ ii };
      // cout << "wsize: " << wsize << " wordsize: " << wordssize << endl;
      while (right < s.size() - wsize + 1) {
        // Move faster pointer one step forward
        std::string rightStr = s.substr(right, wsize);
        right += wsize;
        // cout << "window: [" << left << ", " << right << ")" << endl;
        // Update the window if necessary
        if (needed.count(rightStr) > 0) {
          if (window.count(rightStr) == 0) {
            window.emplace(rightStr, 1);
          } else {
            window[rightStr]++;
          }
          if (window[rightStr] == needed[rightStr]) {
            valid++;
          }
          // cout << "   String to add: "<< rightStr <<  ", valid now: " << valid << endl;
        }
        // Try to shrink the window
        while (right - left >= wordssize) {
          if (valid == needed.size()) {
            res.push_back(left);
          }
          std::string leftStr = s.substr(left, wsize);
          left += wsize;
          if (needed.count(leftStr) > 0) {
            if (window[leftStr] == needed[leftStr]) {
              valid--;
            }
            window[leftStr]--;
          }
        }
      }
    }
    return res;
  }
};

Complexity

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

T: O(n∗m∗k) S: O(m)

watermelonDrip commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        n = len(s)
        m = len(words)
        w = len(words[0])
        dict_map = dict()
        for word in words:
            dict_map[word] = dict_map.get(word,0) + 1
        res = []
        if n < m*w:
            return []
        i = 0
        for j in range(w*m,n+1):
            dict_w = dict()
            tmp = s[i:j]
            for k in range(0,len(tmp),w):
                dict_w[tmp[k:k+w]] = dict_w.get(tmp[k:k+w],0 ) + 1
            if dict_w == dict_map:
                res.append(i)
            i+=1
        return res
ginnydyy commented 2 years ago

Problem

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

Notes

Solution

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> result = new ArrayList<>();
        Map<String, Integer> wordsMap = new HashMap<>();
        for(String word: words){
            wordsMap.put(word, wordsMap.getOrDefault(word, 0) + 1);
        }

        int wordLen = words[0].length();
        int wordsNum = words.length;

        for(int i = 0; i < s.length() - wordLen * wordsNum + 1; i++){
            String curr = s.substring(i, i + wordLen * wordsNum);
            Map<String, Integer> currMap = new HashMap<>();

            int j = 0;
            for(; j < curr.length(); j += wordLen){
                String slice = curr.substring(j, j + wordLen);

                if(!wordsMap.containsKey(slice)){
                    break;
                }

                currMap.put(slice, currMap.getOrDefault(slice, 0) + 1);
                if(currMap.get(slice) > wordsMap.get(slice)){
                    break;
                }
            }

            if(j == curr.length()){
                result.add(i);
            }
        }

        return result;
    }
}

Complexity

leungogogo commented 2 years ago

LC30. Substring with Concatenation of All Words

Method. Map + Sliding Window

Main Idea

First, use a map freqMap to count the frequencies of all words.

Then use sliding window to find all possible substrings, notice all strings in words have the same length, so we can increment the left/right boundary by l, where l is the length of each word.

Code

Space: O(n), for map.

carsonlin9996 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;
    }
}
zhiyuanpeng commented 2 years ago
class Solution(object):
    def findSubstring(self, s, words):
        hash = {}
        res = []
        wsize = len(words[0])

        for str in words:
            if str in hash:
                hash[str] += 1
            else:
                hash[str] = 1
        for start in range(0, len(words[0])):
            slidingWindow = {}
            wCount = 0
            for i in range(start, len(s), wsize):
                word = s[i : i + wsize]
                if word in hash:
                    if word in slidingWindow:
                        slidingWindow[word] += 1
                    else:
                        slidingWindow[word] = 1
                    wCount += 1
                    while hash[word] < slidingWindow[word]:
                        pos = i - wsize * (wCount - 1)
                        removeWord = s[pos : pos + wsize]
                        print i, removeWord
                        slidingWindow[removeWord] -= 1
                        wCount -= 1
                else:
                    slidingWindow.clear()
                    wCount = 0
                if wCount == len(words):
                    res.append(i - wsize * (wCount - 1))

        return res

O(mn)

yan0327 commented 2 years ago

思路: 今天又是不会然后学学的一天。 滑动窗口 + 哈希表的题型整体思路: 先把要查的子表遍历一下,用一个哈希表装每个单词的次数,用Allcount装整体单词出现的次数。

审题: 本题规定了,子串的单词数量相等,因此我们可以每次移动单词的步数 因此,大循环为 0 < n < len(word) 小滑动窗口是 left , right := i, i+len(word),然后for循环看right是否≤len(s),用第二个哈希表装字串值 窗口里面,每次都把最后的s[right, right - lne(word)]的单词拿出来,若该单词是字串的单词,则临时哈希表对应的单词次数++ 如果此时单词的数量与字串单词的数量相等,则Count ++ 然后当长度大于字串长度时,左指针进行移动,同样取左子串出来,如果临时里面有则--,如果临时的单词次数不等于字串单词次数,则Count-- 最后,只有Count ==AllCount 且 字串长度 right - left == oneWordL *wL 则添加到最终数组里

func findSubstring(s string, words []string) []int {
    wL := len(words)
    oneWordL := len(words[0])
    hm := make(map[string]int)
    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++{
        Count := 0
        tmpHm := map[string]int{}
        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++
                }
            }
            for right - left > oneWordL * wL{
                str := s[left:left+oneWordL]
                if _, ok := tmpHm[str];ok{
                    tmpHm[str]--
                }
                if tmpHm[str]+1 == hm[str]{
                    Count--
                }
                left += oneWordL
            }
            if Count == Allcount && right - left == oneWordL * wL{
                ans = append(ans, left)
            }
        }
    }
    return ans
}

时间复杂度:O(nmk) 空间复杂度:O(n)

leolisgit commented 2 years ago

思路

这道题就是滑动窗口题目的一个变体。窗口内的字符组成的substring由给出的word组成。 所以,窗口右边界每次前进一个word的长度,但是左边每次前进一个char的长度。

  1. 从左向右遍历,每次取一个word,判断是否在hashmap中
    1. 如果在,就更新出现的频次
    2. 如果不在,说明这个word没办法组成合法的substring。可以跳出循环,将左边界向右移动一个char
  2. 如果hashmap中的所有元素都被移除,说明找到了一个解

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> ret = new ArrayList<>();
        if (s == null || s.length() == 0) return ret;

        Map<String, Integer> map = new HashMap<>();
        for (String w : words) {
            map.put(w, map.getOrDefault(w, 0) + 1);
        }
        int len = words[0].length();
        int n = words.length;
        for (int i = 0; i <= s.length() - n * len; i++) {
            int start = i;
            int end = i;
            HashMap<String, Integer> copy = new HashMap<>(map);
            while (end <= s.length() - len) {
                String word = s.substring(end, end + len);
                end += len;
                if (!copy.containsKey(word)) {
                    break;
                }
                copy.put(word, copy.get(word) - 1);
                if (copy.get(word) == 0) {
                    copy.remove(word);
                }
                if (copy.isEmpty()) {
                    ret.add(start);
                }
            }
        }
        return ret;
    }
}

复杂度

时间:O(n*m)
空间:O(m)

sxr000511 commented 2 years ago

/**

function match(str, wordMap, wordNum, wordLen) { let map = new Map(); for (let i = 0; i < wordNum; i++) { let word = str.substring(i wordLen, (i + 1) wordLen); let count = map.has(word) ? map.get(word) : 0; map.set(word, count + 1); } let matchflag = true; for (let [key, value] of wordMap) { if (!map.has(key) || map.get(key) !== value) { matchflag = false; } } return matchflag; }

ai2095 commented 2 years ago

LC30. Substring with Concatenation of All Words

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

Topics

Similar Questions

Easy

Medium

思路

Use one hashtable to store the word count. Scan the s by sliding window.

代码 Python

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []
        # find word counter dict
        d = collections.Counter(words)

        word_len, word_num, result = len(words[0]), len(words), []
        # sliding windows
        for i in range(0, len(s) - word_len * word_num + 1):
            temp_d = copy.copy(d)
            for j in range(word_num):
                w = s[i+j*word_len : i+(j+1)*word_len]
                if w in temp_d:         
                    temp_d[w] -=1
                    if temp_d[w] == 0:
                        del temp_d[w]
                else:
                    break
            if len(temp_d) == 0:
                result.append(i)

        return result

复杂度分析

时间复杂度: O(NKM) N=len(s), K=word_len M=word_sum
空间复杂度:O(K*M)

taojin1992 commented 2 years ago

Understand:

data range words can contain duplicates

Plan:

build map of <word, freq>
for each start of subtring of length = unitLen*wordNum
- for each word, populate the map
- do pruning: not in given dictionary, > required freq
if find all the words, add start to result indices

Time:
O(words.length()) + O(s.length() - unitLen*wordNum) * [O(unitLen*wordNum) + O(wordNum) * O(unitLen)]
-> O(s.length() - unitLen*wordNum) * O(wordNum) * O(unitLen)

Space:two maps
O(number of unique words)

Code:

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> indices = new ArrayList<>();
        // get freq 
        Map<String, Integer> wordFreq = new HashMap<>();
        for (String word : words) {
            wordFreq.put(word, wordFreq.getOrDefault(word, 0) + 1);
        }

        int unitLen = words[0].length(), wordNum = words.length;
        // sliding window
        // len to check = word.length * num of words
        // index to check: [0, end], s.length()-1 - end + 1=word.length * num of words
        // end = s.length() - unitLen*wordNum

        for (int start = 0; start <= s.length() - unitLen*wordNum; start++) {
            String curSub = s.substring(start, start + unitLen*wordNum);
            // use a curFreqMap to see the difference
            Map<String, Integer> curWordFreq = new HashMap<>();
            int wordIndex = 0;
            // within curSub
            for (wordIndex = 0; wordIndex + unitLen <= curSub.length(); wordIndex += unitLen) { // note the incremental
                String curUnit = curSub.substring(wordIndex, wordIndex + unitLen);
                // not in given dictionary
                if (!wordFreq.containsKey(curUnit)) {
                    break;
                }
                curWordFreq.put(curUnit, curWordFreq.getOrDefault(curUnit, 0) + 1);
                // > required freq
                if (curWordFreq.get(curUnit) > wordFreq.get(curUnit)) {
                    break;
                }
            }

            // if finish the check, bingo
            if (wordIndex == curSub.length()) {
                indices.add(start);
            }

        }

        return indices;
    }
}
JK1452470209 commented 2 years ago

思路

暴力枚举匹配,有点滑动窗口的思想。使用哈希表记录单词的频率,然后对字符串进行遍历。每个长度单词相同,让题目更加简单一些 每次对字符串进行一个单词的匹配,如果匹配上就更新频率 使用k标记消除的次数。如果k为0则表示哈希表中全部匹配上

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {     
    List<Integer> res = new ArrayList<>();                         
    if ("".equals(s) || words == null){                            
        return res;                                                
    }                                                              
    HashMap<String, Integer> map = new HashMap<>();                
    Integer sum = 0;                                               
    Integer wordLength = words[0].length();                        
    for (String word : words) {                                    
        map.put(word,map.getOrDefault(word,0) + 1);                
        sum += word.length();                                      
    }                                                              
    for (int i = 0; i < s.length() - sum + 1; i++) {               
        Map<String, Integer> copyMap = new HashMap<>(map);         
        int k = words.length;                                      
        int j = i;                                                 
        while (k > 0){                                             
            String str = s.substring(j, j + wordLength);           
            if (!copyMap.containsKey(str) || copyMap.get(str) < 1){
                break;                                             
            }                                                      
            copyMap.put(str,copyMap.get(str) - 1);                 
            k--;                                                   
            j += wordLength;                                       
        }                                                          
        if (k == 0){                                               
            res.add(i);                                            
        }                                                          
    }                                                              
        return res;                                                    
    }                                                                  
}

复杂度

时间复杂度:O(N^2)

空间复杂度:O(N)

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

复杂度

iambigchen commented 2 years ago

思路

用两个map,第一个map存words每个出现的次数,另外一个map存当前字符串下每个出现次数,如果两个map对应的次数相同,也认为该字符串起始位置满足条件

代码

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    let chartLength = words[0].length
    let wordLength = words.length
    let map = {}
    let res = []
    words.forEach(key => {
        map[key] = map[key]? map[key] + 1 : 1
    })
    for(let i =0; i<s.length - wordLength * chartLength + 1; i++) {
        let y = s.slice(i, i + wordLength * chartLength)
        let temp = {}
        for (let j =0; j < y.length; ) {
            let cur = y.slice(j, chartLength + j)
            if (!map[cur]) {
               break
            } else {
                temp[cur] = temp[cur] ? temp[cur] + 1 : 1
            }
            if ( temp[cur] > map[cur] ) {
                break
            }
            j = j + chartLength
            if (j == y.length) {
                res.push(i)
            }
        }
    }
    return res
};

复杂度

时间复杂度 O(nmk) 空间复杂度 O(m)

chen445 commented 2 years ago

思路

First, we can use hashmap to keep track count of each word. We iterate through the sentence. At each index, we check whether the window start with that index can be concatenated with the same words exactly once using the hashmap. If it is true, we add the index into the result.

代码

var findSubstring = function(s, words) {
    let hash_map={}
    words.forEach(function(word,i){
        if(hash_map[word] === undefined){
            hash_map[word]=1
        }else{
            hash_map[word]+=1
        }
    })

    let left=0
    let total_length=(words.length*words[0].length)
    const result = []
    while (left<=s.length-total_length){
        let hash_map_copy = Object.assign({}, hash_map);
        let word_length=words[0].length
        let count = words.length
        let start= left
        while(start<start+total_length){
            if(hash_map_copy[s.slice(start,start+word_length)]===undefined ||
                hash_map_copy[s.slice(start,start+word_length)] === 0) {
                break
            }else{
                hash_map_copy[s.slice(start,start+word_length)]-=1
                count -= 1
                start+=word_length
            }
        }

        if (count === 0) {
            result.push(left)
        }
        left += 1
    }
    return result
};

复杂度

Time:O(m(n-m)k) where n is the length of string, and m is the length of words

Space:O(m)

carterrr commented 2 years ago

vclass Solution {     public List findSubstring(String s, String[] words) {         int stepLength = words[0].length();         int stepCnt = words.length;         int windowLength = stepLength * stepCnt;         Map<String, Integer> wordCntMap = new HashMap<>();         for(String word : words){             wordCntMap.put(word, wordCntMap.getOrDefault(word, 0 )  + 1);         }         List res = new ArrayList<>();         for(int i = 0; i < stepLength; i++ ) {// 多起点  0 - (步长 -1) 都是起点  否则如   sfoothebarf这种会跳过             Map<String, Integer> cntMap = new HashMap<>();             // 每个起点开始 每次后移stepLength  如果距离已经到达了window  就删一个 加一个  判断两个map是否相等             //                                          没到达window      就一直向windowput             //  最后判断两个map是否相等             for(int j = i ; j <= s.length() - stepLength; j += stepLength) {  // 下一个单词  j -> j + stepLength -1  因此是 j <=  而不是  <                 // window满后 窗口后移之后移除之前的单词                 if(j >= i + windowLength) {                     String prev = s.substring(j - windowLength, j - windowLength + stepLength);                     Integer cnt = cntMap.get(prev);                     if(cnt == 1) cntMap.remove(prev);                     else cntMap.put(prev, cnt - 1);                 }                 String newWord = s.substring(j, j + stepLength);                 cntMap.put(newWord, cntMap.getOrDefault(newWord, 0) + 1);

                if(wordCntMap.equals(cntMap)) {                     res.add(j - windowLength + stepLength);                 }             }         }         return res;     } } 、

Richard-LYF commented 2 years ago

class Solution: def findSubstring(self, s: str, words: List[str]) -> List[int]: from collections import Counter if not s or not words:return [] one_word = len(words[0]) all_len = len(words) * one_word n = len(s) words = Counter(words) res = [] for i in range(0, n - all_len + 1): tmp = s[i:i+all_len] c_tmp = [] for j in range(0, all_len, one_word): c_tmp.append(tmp[j:j+one_word]) if Counter(c_tmp) == words: res.append(i) return res

chaggle commented 2 years ago

title: "Day 23 30. 串联所有单词的子串" date: 2021-10-02T13:49:39+08:00 tags: ["Leetcode", "c++", "unordered_map"] categories: ["algorithm"] draft: true


30. 串联所有单词的子串

题目

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

 

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
 

提示:

1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成

题目思路

  • 1、困难题目,开局思考半小时,发现应该可以使用滑动窗口的解法
  • 2、具体思想为使用两个 unordered_map,一个 up 来记录总的 words 里单词和对应的数量,另一个 ump 用来记录遍历的滑动窗口内的 words 中的单词和对应的数量。
  • 3、遍历是增加一个单词长度,按照 ws 去移动窗口,对于每次档次遍历,维持窗口 l = r
  • 4、若单词在 ump 里不存在,重置窗口并清空 ump;
  • 5、单词在 ump 里存在,有两种清空,单词出现的次数超出 ump 中的那一个单词的次数,所以需要将左边界增大,即右移 l,如果 cnt 都满足即等于 ns,则插入 l 作为当前的答案值
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans;
        int n = s.size();
        if(n <= 0 || words.empty()) return ans;
        int ws = words[0].size();       //words中的word的长度,为统一长度
        int ns = words.size();          //words中的word数量
        unordered_map<string, int> up; //保存words中的word
        for(auto & w : words)
        {
            up[w]++;    //记录每一个单词出现的次数
        }
        for(int i = 0; i < ws; i++)
        {
            int l = i, r = i; //从左开始
            int cnt = 0;    //下文ump存放的单词总数!
            unordered_map<string, int> ump;
            while(r + ws <= n)  //右边长度加每个单词长度要小于s的全长
            {
                string res = s.substr(r, ws); //在右边界加入一个单词
                r += ws;        //扩展右边界
                if(up.find(res) != up.end()) //查看res在up中的位置
                {
                    ump[res]++;
                    cnt++;
                    while(ump[res] > up[res])// 需要检查数量是否超过,超过则要右移left
                    {                        //来缩小窗口
                        string tmp = s.substr(l, ws);
                        l += ws;
                        cnt--;
                        ump[tmp]--;
                    }
                    if(cnt == ns) ans.push_back(l);
                }
                else //未出现,舍弃此单词。清空
                {
                    l = r;
                    cnt = 0;
                    ump.clear();
                }
            }
        }
        return ans;
    }
};

复杂度

Bingbinxu commented 2 years ago

思路 官方思路:以word里面总字符串长度,对s里面的子字符串进行遍历 每取s中子字符串的长度,就用map来检查是否能恰好用word里面的构成,不能右打断,不能右数量的不一致,如果有就break 代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        map<string, int> mapp;
        int count = words.size();
        if(words.empty() || count == 0)
        {
            return res;
        }
        for(auto s : words)
        {
            mapp[s] += 1;
        }
        int slen = s.size(), wordlen = words[0].size();
        int match = 0;
        int sumlen = wordlen*count;
        for(int i = 0;i < slen - sumlen + 1;i++)
        {
            string cur = s.substr(i, sumlen);
            map<string, int> temp;
            int j = 0;
            for(;j < cur.size();j+=wordlen)
            {
                string word = cur.substr(j, wordlen);
                if(!mapp.count(word))
                {
                    break;
                }
                temp[word] += 1;
                if(temp[word] > mapp[word])
                {
                    break;
                }
            }
            if(j == cur.size())
            {
                res.push_back(i);
            }
        }
        return res;
    }
};

复杂度 时间复杂度:排序算法O((N-mk)m) 空间复杂度:存储所有的节点O(m),m为words个数

Huangxuang commented 2 years ago

题目:30. Substring with Concatenation of All Words

思路

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList();
        if (s.length() < 1) {
            return null;
        }

        int len = 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(); i++) {
            int left = i, right = left + len - 1;
            HashMap<String, Integer> temp = new HashMap(map);
            int solu = left;
            while (right < s.length()) {
                String sub = s.substring(left, right + 1);
                //System.out.println(sub);
                if (!temp.containsKey(sub)) {
                    break;
                }
                int count = temp.get(sub);
                if (count > 1) {
                    temp.put(sub, count - 1);
                } else {
                    temp.remove(sub);
                }
                if (temp.isEmpty()) {
                    res.add(solu);
                    break;
                }
                left = right + 1;
                right = left + len - 1;

            }
        }

        return res; 
    }
}

复杂度分析

okbug commented 2 years ago

代码

语言:C++

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (words.size() == 0) return res;
        int n = s.size(), m = words.size(), w = words[0].size();
        unordered_map<string, int> total;
        for (string& word: words) total[word] ++;

        for (int i = 0; i < w; i++) {
            unordered_map<string, int> wd;
            int cnt = 0;
            for (int j = i; j + w <= n; j += w) {
                if (j >= i + m * w) {
                    auto word = s.substr(j - m * w, w);
                    wd[word] -- ;
                    if (wd[word] < total[word]) cnt --;
                }

                auto word = s.substr(j, w);
                wd[word] ++;
                if (wd[word] <= total[word]) cnt ++ ;
                if (cnt == m) res.push_back(j - (m - 1) * w);
            }
        }

        return res;
    }
};
kennyxcao commented 2 years ago

30. Substring with Concatenation of All Words

Intuition

Code

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
const findSubstring = function(s, words) {
  if (!s || !words || s.length === 0 || words.length === 0) return [];
  const counter = new Map();
  for (const word of words) {
    counter.set(word, (counter.get(word) || 0) + 1);
  }
  const ans = [];
  const sLen = s.length;
  const wordsSize = words.length;
  const wordLen = words[0].length;
  const limit = sLen - wordsSize * wordLen + 1;
  for (let i = 0; i < limit; ++i) {
    const substr = s.substring(i, i + wordsSize * wordLen);
    if (isConcat(substr, counter, wordLen)) {
      ans.push(i);
    }
  }
  return ans;
};

function isConcat(candidate, counter, wordLen) {
  const seen = new Map();
  for (let i = 0; i < candidate.length; i += wordLen) {
    const word = candidate.substring(i, i + wordLen);
    seen.set(word, (seen.get(word) || 0) + 1);
    if (!counter.has(word) || seen.get(word) > counter.get(word)) return false;
  }
  return true;
}

Complexity Analysis

comst007 commented 2 years ago

30. 串联所有单词的字串


思路

参考leetcode题解
滑动窗口 + 哈希表

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int n = s.size();
        int m = words.size();
        if(m == 0) return vector<int>();
        int w = words[0].size();
        unordered_map<string, int> words_freq;
        for(auto& cur: words){
            words_freq[cur] ++;
        }

        int ii, jj, cnt_res;
        vector<int> ans;
        for(ii = 0; ii < w; ++ ii){
            unordered_map<string, int> s_freq;
            cnt_res = 0;
            for(jj = ii; jj + w <= n; jj += w){
                if(ii + m * w <= jj){
                    string sub_s = s.substr(jj - w * m, w);

                    if(words_freq.count(sub_s) && s_freq[sub_s] <= words_freq[sub_s]){
                        cnt_res --;
                    }

                    s_freq[sub_s] --;
                }
                string sub_s = s.substr(jj, w);

                if(words_freq.count(sub_s) && s_freq[sub_s] < words_freq[sub_s]){
                    cnt_res ++;
                }

                s_freq[sub_s] ++;

                if(cnt_res == m){
                    ans.push_back(jj - (m - 1) * w);
                }
            }   
        }
        return ans;
    }
};

复杂度分析

n为字符串s的长度,m为words单词的个数,w为words中每个单词的长度。

machuangmr commented 2 years ago

题目30. 串联所有单词的子串

思路

thinkfurther commented 2 years ago

思路

将words里的每个word统计频率存放在dict里

然后滑动长度为words字符个数的窗口,比较里面的字符是否在words里

若全部在里面,则增加起始位置到结果列表里面

代码

class Solution:
    import collections
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []

        result = []

        words_map = collections.defaultdict(int)

        for word in words:
            words_map[word] += 1

        sLen = len(s)
        wordLen = len(words[0])
        count = len(words)
        wordsLen = wordLen * count

        for i in range(sLen - wordLen * count + 1):
            cur = s[i : i + wordsLen]
            temp = collections.defaultdict(int)
            j = 0
            for j in range(0, wordsLen + 1, wordLen):
                word = cur[j : j + wordLen]
                if word not in words_map:
                    break
                temp[word] += 1
                if temp[word] > words_map[word]:
                    break
            if j == wordsLen:
                result.append(i)

        return result

复杂度

时间复杂度 :O(Nmk)

空间复杂度:O(m)

septasset commented 2 years ago

思考

看题解复现

关键点

  1. 暴力解→哈希表优化
  2. 双哈希表的相等

代码(Python)

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        word_len = len(words[0])
        cand_len = word_len * len(words)
        ans = []
        if len(s) < cand_len: return ans

        words_occrs = dict()
        for word in words:
            if word in words_occrs: words_occrs[word] += 1
            else: words_occrs[word] = 1

        for i in range(len(s)):
            if i+cand_len<=len(s):
                cand_occrs = dict()
                j = i
                while j<i+cand_len:
                    word = s[j:j+word_len]
                    if word not in words_occrs: break
                    else:
                        if word in cand_occrs: cand_occrs[word] += 1
                        else: cand_occrs[word] = 1
                        if cand_occrs[word] > words_occrs[word]: break
                    j+=word_len
                if j==(i+cand_len): ans.append(i)
        return ans

复杂度分析

m-z-w commented 2 years ago
var findSubstring = function(s, words) {
    let dict = new Map()
    let len = words[0].length
    let count = words.length
    let left = 0
    const res = []
    words.forEach(word => {
        dict.set(word, (dict.get(word) || 0) + 1)
    })
    for (let i = 0; i < s.length - len * count + 1; i++) {
        let cur = s.slice(i, i + len * count)
        let tmp = new Map()
        let j = 0
        for (; j < cur.length; j += len) {
            let word = cur.slice(j, j + len)
            if (!dict.has(word)) {
                break
            }
            tmp.set(word, (tmp.get(word) || 0) + 1)
            // 剪枝
            if (tmp.get(word) > dict.get(word)) {
                break
            }
        }
        if (j === cur.length) {
            res.push(i)
        }
    }
    return res
};
n为字符串s的长度,m为words的数组元素个数,k为words内单个word的长度
时间:O(n * m * k)
空间:O(m)
15691894985 commented 2 years ago

day23 30. 串联所有单词的子串

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

思路:

   class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        allwords = collections.Counter(words)
        lengthword = len(words[0])
        word_num = len(words)
        n = len(s)
        res = []
        for i in range(n-lengthword * word_num + 1):
            subwords = collec
            tions.defaultdict(int)
            index = i
            #遍历
            while index < i + lengthword*word_num :
                curwords = s[index :index +lengthword]
                if curwords not in allwords or subwords[curwords] ==allwords[curwords]: #选取的当前字串不在words里面,或者计数已经满了
                    break
                subwords[curwords] += 1
                index = index + lengthword
            if index == i + lengthword*word_num:
                res.append(i)
        return res

复杂度分析:

ChenJingjing85 commented 2 years ago

思路

双指针固定滑动窗口,哈希表记录词表频率,每次滑动一个窗口检查是否在哈希表内,若在,哈希表减去频率,继续滑动窗口,直到哈希表为空表示完全匹配上;若不在,移动初始指针

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList();
        if(null == s || s.equals ("") || null == words || words.length==0){
            return res;
        }
        //map: <word,count>
        Map<String, Integer> map = new HashMap();
        for(String word : words){
            if(map.containsKey(word)){
                int count = map.get(word);
                map.put(word, ++count);
            }else{
                map.put(word, 1);
            }
        }
        Map<String, Integer> mapCopy = new HashMap(map);
        int len = words[0].length();
        int begin = 0;

        while (begin < s.length()){
            int i = begin;
            int j = i+len;
            mapCopy = new HashMap(map);
            while(j <= s.length()){
                String sub = s.substring(i,j);
                if(mapCopy.containsKey(sub)){
                    int count = mapCopy.get(sub);
                    if(count > 1){
                        mapCopy.put(sub, --count);
                    }else{
                        mapCopy.remove(sub);
                    }
                    i = j;
                    j = i+len;
                    if(mapCopy.isEmpty()){
                        res.add(begin);
                        break;
                    }
                }else{
                    break;
                }
            }
            begin++;
        }

        return res;
    }
}

复杂度分析

biscuit279 commented 2 years ago

统计words的词频,将s切分成words的长度,统计词频后与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(mnk) 空间复杂度:O(m)

Moin-Jer commented 2 years ago

思路


使用哈希表记录单词出现的次数,从s 中截取固定长度的字符串,并添加到哈希表中,使用滑动窗口来判断当前窗口中的字串组成的哈希表是否和words组成的哈希表相等

代码


class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        Map<String, Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        List<Integer> ans = new ArrayList<>();
        int wordLen = words[0].length();
        for (int i = 0; i < wordLen; ++i) {
            int l = i, r = i, wordCount = 0;
            Map<String, Integer> tmp = new HashMap<>();
            while (r + wordLen <= s.length()) {
                String tmpWord = s.substring(r, r + wordLen);
                tmp.put(tmpWord, tmp.getOrDefault(tmpWord, 0) + 1);
                ++wordCount;
                r += wordLen;
                while (tmp.getOrDefault(tmpWord, 0) > map.getOrDefault(tmpWord, 0)) {
                    String leftWord = s.substring(l, l + wordLen);
                    tmp.put(leftWord, tmp.getOrDefault(leftWord, 0) - 1);
                    --wordCount;
                    l += wordLen;
                }
                if (wordCount == words.length) {
                    ans.add(l);
                }
            }
        }
        return ans;
    }
}

复杂度分析


RonghuanYou commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        word_len = len(words[0])
        words_len = len(words)

        total = word_len * words_len
        ans=[]
        wordCount = {}
        for word in words:
            wordCount[word] = wordCount.get(word, 0) + 1

        for i in range(len(s) - total + 1):
            s1 =s[i : i + total]          
            subs = [s1[j : j + word_len] for j in range(0, len(s1), word_len)]  
            if Counter(subs) == wordCount:
                ans.append(i)
        return ans
mannnn6 commented 2 years ago

思路

遍历子串

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
    List<Integer> res = new ArrayList<Integer>();
    int wordNum = words.length;
    if (wordNum == 0) {
        return res;
    }
    int wordLen = words[0].length();
    HashMap<String, Integer> allWords = new HashMap<String, Integer>();
    for (String w : words) {
        int value = allWords.getOrDefault(w, 0);
        allWords.put(w, value + 1);
    }
    for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {
        HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
        int num = 0;
        while (num < wordNum) {
            String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen); 
            if (allWords.containsKey(word)) {
                int value = hasWords.getOrDefault(word, 0);
                hasWords.put(word, value + 1);
                if (hasWords.get(word) > allWords.get(word)) {
                    break;
                }
            } else {
                break;
            }
            num++;
        }
        if (num == wordNum) {
            res.add(i);
        }
    }
    return res;
}
}

复杂度

时间复杂度:O(n*m) 空间复杂度:O(m)

wenlong201807 commented 2 years ago

解题思路


思路很简单,使用滑动窗口,当窗口的大小和words的长度一致时,使用map存储该窗口根据单词长度划分的单词,将map和words构成的map进行对比,满足要求则输出left。

代码块

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function (s, words) {
  let left = 0,
    right = 0;
  let slen = s.length;
  let wordLen = words[0].length;
  let wordNum = words.length;
  let wlen = wordNum * wordLen;
  let wordMap = new Map();
  for (let word of words) {
    let count = wordMap.has(word) ? wordMap.get(word) : 0;
    wordMap.set(word, count + 1);
  }
  let res = [];
  while (right < slen) {
    right++;
    if (right - left === wlen) {
      if (match(s.substring(left, right), wordMap, wordNum, wordLen)) {
        res.push(left);
      }
      left++;
    }
  }
  return res;
};

function match(str, wordMap, wordNum, wordLen) {
  let map = new Map();
  for (let i = 0; i < wordNum; i++) {
    let word = str.substring(i * wordLen, (i + 1) * wordLen);
    let count = map.has(word) ? map.get(word) : 0;
    map.set(word, count + 1);
  }
  let matchflag = true;
  for (let [key, value] of wordMap) {
    if (!map.has(key) || map.get(key) !== value) {
      matchflag = false;
    }
  }
  return matchflag;
}

时间复杂度和空间复杂度

last-Battle commented 2 years ago

思路

关键点

代码

C++ Code:


class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        unordered_map<string, int> allWordsMap;
        for (auto& v : words) {
            ++allWordsMap[v];
        }

        int num = words.size();
        int onelen = words[0].length();
        vector<int> res;
        if (s.length() < num * onelen) {
            return res;
        }

        for (int left = 0; left < s.length() - num * onelen + 1; ++left) {
            unordered_map<string, int> nowWordsMap;
            int right = left;
            while (right < left + num * onelen) {
                auto cur = s.substr(right, onelen);
                if (allWordsMap.find(cur) == allWordsMap.end() 
                    || nowWordsMap[cur] == allWordsMap[cur]) {
                    break;
                }

                ++nowWordsMap[cur];
                right += onelen;
            }

            if (right == left + num * onelen) {
                res.emplace_back(left);
            }
        }

        return res;
    }
};

复杂度分析

令 n 为数组长度。

mokrs commented 2 years ago

思路:

使用滑动窗口和两个哈希表,一个哈希表记录words中每个word的个数,一个记录滑动窗口内word已经出现的个数

首先遍历字符串,找到第一个在单词列表中出现的索引位置。根据单词的长度的个数,计算出滑动窗口的大小。在滑动窗口中遍历是否符合每个单词按照要求的个数出现。

 vector<int> findSubstring(string s, vector<string>& words) {
     vector<int> res;
     unordered_map<string, int> mp_index;
     unordered_map<string, int> mp_cnt;
     for (string &word : words){
         mp_index[word] = 0;
         mp_cnt[word]++;
     }
     int word_len = words[0].length();

     int left = 0;
     int right = left + words.size() * word_len;

     while (right <= s.length()){
         string word = s.substr(left, word_len);
         //找到第一个
         if (mp_cnt.count(word)){               
             for (auto it = mp_index.begin(); it != mp_index.end(); ++it){
                 it->second = 0;
             }              
             mp_index[word]++;  

             int i;
             for (i = left + word_len; i < right; i += word_len){
                 string word = s.substr(i, word_len);
                 if (mp_cnt.count(word) && mp_cnt[word] > mp_index[word]){
                     mp_index[word]++;                  
                 }
                 else{
                     break;
                 }                  
             }

             if (i == right){
                 res.push_back(left);
             }
         }

         ++left;
         ++right;
     }      

     return res;        
 }
LareinaWei commented 2 years ago

Thinking

Referring to the solution given. Using a hashmap to record the time each word appear in the list, and using a sliding window of length each word length * number of words to check If the substring meets the requirement. Use another hashmap to store the number of times each word appear in the substring and compare it with the previous hashmap.

Code

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        wlen = len(words[0])
        dic = {}
        for word in words:
            if word not in dic:
                dic[word] = 0
            dic[word] += 1

        count = len(words)
        l = len(s)
        res = []
        for i in range(l - count * wlen + 1):
            curr_str = s[i: i + wlen * count]
            curr_dic = {}
            for j in range(0, len(curr_str), wlen):
                word = curr_str[j: j + wlen]
                if word not in dic:
                    break
                if word not in curr_dic:
                    curr_dic[word] = 0
                curr_dic[word] += 1
                if curr_dic[word] > dic[word]:
                    break
            if dic == curr_dic:
                res.append(i)

        return res

Complexity

Time complexity:: O(n) Space complexity: O(n)

TimmmYang commented 2 years ago

思路

先用暴力解一下。使用哈希表统计words的单词频次,打开滑窗,遍历字符串,验证结果。后面可以开n个哈希表存所有遍历可能性(n为words中单词长度),这样就没有了循环嵌套。

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        res = []
        size = len(words[0])
        check = {}
        for j, word in enumerate(words):
            if word not in check:
                check[word] = 1
            else:
                check[word] += 1

        for i, ch in enumerate(s):
            tmp_dic = check.copy()
            tmp = i
            while True:
                if s[tmp:tmp+size] in tmp_dic:
                    tmp_dic[s[tmp:tmp+size]] -= 1
                    if tmp_dic[s[tmp:tmp+size]] == 0:
                        del tmp_dic[s[tmp:tmp+size]]
                    tmp += size
                else:
                    break
            if not tmp_dic:
                res.append(i)
        return res

复杂度

时间:O(M^2/N),M为s长度,N为words中word的长度 空间:O(L),L为words长度

iamtheUsername commented 2 years ago
思路
建立两个hashmap,将words中的单词存入hashmap1中
遍历s,将s中单词记录到hashmap2中
hashmap2中单词的value与hashmap1中的比较,相同就扫描下一个单词
不同则直接跳出
代码[C++]
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if(words.size() == 0)
        return vector<int>{};

        int wordsNum = words.size();
        int wordsSinglesize = words[0].size();
        int wordsToatallen = wordsNum * wordsSinglesize;

        unordered_map<string,int> m1;
        for(int i = 0;i < wordsNum;i++){
            m1[words[i]]++;
        }

        unordered_map<string,int> m2;
        for(int i = 0;(i + wordsToatallen) <= s.size();i++){
            int j = 0;
            for(j = i;j < (i + wordsToatallen);j =j + wordsSinglesize){
                string str0 = s.substr(j,wordsSinglesize);
                if(m1[str0] == 0){
                    break;
                }else{
                    m2[str0]++;
                    if(m1[str0] < m2[str0])
                    break;
                } 
            }
            if(j == i + wordsToatallen)
            res.push_back(i);
            m2.clear();
        }
        return res;
    }
};
复杂度分析
时间复杂度:O(n*m) n为s的长度,m为words内单词的个数
空间复杂度:O(m)取决于words内单词的个数m
wangwiitao commented 2 years ago
var findSubstring = function(s, words) {
    if (!s || !words || !words.length) return [];
    let windows = {}, needs = {}, oneWordLen = words[0].length;
    for (let w of words) {
        needs[w] ? needs[w]++ : needs[w] = 1;
    }
    let l = 0, r = 0, count = 0, needsKeyLen = Object.keys(needs).length, ans = [];
    for (let i = 0; i < oneWordLen; i++) {
        windows = {};
        r = l = i;
        count = 0;
        while (r <= s.length - oneWordLen) {
            let w1 = s.slice(r, r + oneWordLen);
            r += oneWordLen;
            if (!needs[w1]) {
                windows = {};
                l = r;
                count = 0;
                continue;
            }
            windows[w1] ? windows[w1]++ : windows[w1] = 1;
            if (windows[w1] === needs[w1]) count++;
            while (count === needsKeyLen) {
                if (r - l === oneWordLen * words.length) ans.push(l);
                let w2 = s.slice(l, l + oneWordLen);
                l += oneWordLen;
                if (needs[w2]) {
                    windows[w2]--;
                    if (windows[w2] < needs[w2]) count--;
                }
            }
        }
    }
    return ans;
};
miss1 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;
    }
}
QiZhongdd commented 2 years ago
var findSubstring = function(s, words) {
    let map={};
    for(let i=0;i<words.length;i++){
        let num=map[words[i]]?map[words[i]]:0;
        map[words[i]]=++num;
    }
    let wordSize=words[0].length;
    let len=words.length;
    let allSize=wordSize*len;
    let res=[];
    for(let i=0;i<s.length-allSize+1;i++){
        let str=s.substring(i,i+allSize);
        let partMap={}
        let j=0;
        for(;j<str.length;j+=wordSize){
            let part=str.substring(j,j+wordSize);
            if(!map[part]){
                break;
            }
            let num=partMap[part]?partMap[part]:0;
            partMap[part]=++num;
            if(partMap[part]>map[part]){
                break;
            }
        }
        if(j===str.length){
            res.push(i)
        }
    }
    return res;

};

时间复杂度O(n2),空间复杂O(n)

Maschinist-LZY commented 2 years ago

思路

哈希表+滑动窗口

代码

C++ Code:


 class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        // 设 words中所有单词的长度为 d
        int n = s.size(), m = words.size(), d = words[0].size();
        int len = 0;
        unordered_map<string, int> um;
        for (string w : words) {
            len += w.size();
            um[w]++;
        }

        // init: 初始化长度为 d 的数组
        vector<unordered_map<string, int> > vu(d);
        for (int i = 0; i < d && i + len <= n; i++) {
            for (int j = i; j < i + len; j += d) {
                string w = s.substr(j, d);
                vu[i][w]++;
            }
            if (vu[i] == um) {
                res.emplace_back(i);
            }
        }

        // sliding window: 滑动窗口,每次移动 d 个位置
        for (int i = d; i + len <= n; i++) {
            int r = i % d;
            string wa = s.substr(i - d, d), wb = s.substr(i + len - d, d);
            if(--vu[r][wa] == 0) vu[r].erase(wa);
            vu[r][wb]++;
            if (vu[r] == um) {
                res.emplace_back(i);
            }
        }

        return res;
    }
};

复杂度分析

令 n 为数组长度。

yj9676 commented 2 years ago
public List<Integer> findSubstring(String s, String[] words) {
        if(s == null || s.length() == 0 || words == null || words.length == 0) return Collections.emptyList();
        HashMap<String,Integer> map = new HashMap<>();            // map to record frequency of word 
        int len = words[0].length()*words.length;
        for(String str: words){
            int count = map.getOrDefault(str,0);
            map.put(str,count+1);
        }
        List<Integer> list = new LinkedList<>();
        loop:
        for(int i = 0;i<=s.length() - len;i++){  //O(m - n) time complexity, where m is length of s, n is the length of all word
            HashMap<String,Integer> tempMap = new HashMap<>();    //
            for(int j = 0;j<len;j += words[0].length()){          //O(N) time complexity, where N is length of words
                String sub = s.substring(i+j,i+j+words[0].length());
                if(map.containsKey(sub)){
                    int count = tempMap.getOrDefault(sub,0);
                    tempMap.put(sub,count+1);
                }else continue loop;
            }
            if(map.equals(tempMap)) list.add(i);                    //O(n) time complexity to compare two maps
        }
        return list;
    }
asterqian commented 2 years ago

思路

把string s建立出一个一个为words总长度的滑动窗口,把每一个窗口都分成为words[0]长度的单词,一一加入临时的本窗口有效的map并与原map作比较:1如果不存在可以直接prune;2如果两者的count不同也可以直接prune。最后如果没有被prune则说明当前窗口是符合要求的,加入vector

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.size() == 0 || words.size() == 0) return {};
        vector<int> res;
        unordered_map<string, int> map;
        for (string& s: words) {
            map[s]++;
        }
        int sLen = s.size();
        int wordLen = words[0].size();
        int wordCount = words.size();
        for (int i = 0; i < sLen - wordCount * wordLen + 1; ++i) {
            string curr = s.substr(i, wordLen * wordCount);
            unordered_map<string, int> temp;
            int j = 0;
            for (; j < curr.size(); j += wordLen) {
                string word = curr.substr(j, wordLen);
                if (map.find(word) == map.end()) {
                    break;
                }
                temp[word]++; 
                if (map[word] < temp[word]) {
                    break;
                }
            }
            if (j == curr.size()) {
                res.push_back(i);
            }
        }
        return res;
    }
};

复杂度分析

时间复杂度: O(NMK),把string s分为M*K个部分(M为元素个数,K为元素长度)
空间复杂度: O(N),N为vector元素个数