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

linyang4 commented 2 years ago

思路

预设几个变量 words_len: words数组的长度; word_len: words中每个字符串的长度

步骤

  1. 创建1个map, map的key为words中的每个word, map的value为word在words中出现的次数

    • 例: 如果words=['foo', 'bar', 'foo'], 那么 map = new Map({ 'foo': 2, 'bar': 1 }}
  2. 创建2个索引, left和right, left默认为0, left与right间保持一个定量的关系: right = left + words_len * word_len

    • 例: 如果words中一共有3个word, 每个word的长度为4, 那么默认的right = 0 + 3 * 4 = 12
  3. 从s中截取[left, right]部分的字符串, 把这部分的字符串均分成words_len份, 然后把每部分的子串存进一个新的map中, map的key是长度为word_len的子串, value是子串在[left, right]中出现的次数

  4. 比对2个map, 如果2个map中key和value都是一致的, 就可以认为是有效的答案, 然后把left存进结果数组, left和right分别加1

  5. 在right没到达s的尾部时, 循环第步的过程

代码

var findSubstring = function(s, words) {
    const ans = []
    const singleWordLen = words[0].length
    const windowWidth = singleWordLen * words.length
    const wordsMap = new Map()
    words.forEach((word) => {
        wordsMap.set(word, (wordsMap.get(word) || 0) + 1)
    })
    let left = 0
    let right = windowWidth - 1
    while(right < s.length) {
        const subStrMap = new Map()
        let isValid = true
        for(let i = left; i <= right - singleWordLen + 1; i += singleWordLen) {
            const subStr = s.slice(i, i + singleWordLen)
            if (wordsMap.get(subStr) === undefined) {
                isValid = false
                break
            }
            subStrMap.set(subStr, (subStrMap.get(subStr) || 0) + 1)
        }
        if (isValid) {
            for(let key of subStrMap.keys()) {
                if (subStrMap.get(key) !== wordsMap.get(key)) {
                    isValid = false
                    break
                }
            }
        }

        if (isValid) { ans.push(left) }
        left ++
        right ++
    }
    return ans
};

复杂度

jiaqiliu37 commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        wordlen = len(words[0])
        substringlen = len(words) * wordlen
        hashmap1 = Counter(words)
        ans = []

        for i in range(len(s) - substringlen + 1):
            curwords = []
            for j in range(i, i + substringlen, wordlen):
                curwords.append(s[j:j + wordlen])

            hashmap2 = Counter(curwords)
            if hashmap1 == hashmap2:
                ans.append(i)

        return ans

Time complexity O(n*k) where k is the length of the substring Space complexity O(m) where m is the number of words

biaohuazhou commented 2 years ago

思路

把words中的单词全部取出 放到哈希表中 再从s中取出和word中全部单词拼接长度相同的子串
从子串中截取单个单词长度的字符串 放去另外一个哈希表 比较两个哈希表

代码

    class Solution {
        public List<Integer> findSubstring(String s, String[] words) {
            //输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
            //输出:[6,9,12]
            List<Integer> ans = new ArrayList<>();
            if (s == null || s.length() == 0 || words == null || words.length == 0) {
                return ans;
            }
            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 < s.length() - all_len + 1; i++) {
                String substring = s.substring(i, i + all_len);
                HashMap<String, Integer> map1 = new HashMap<>();
                for (int j = 0; j < all_len; j += one_word) {
                    String s1 = substring.substring(j, j + one_word);
                    //剪枝  加快速度
                    if(!map.containsKey(s1)){
                        break;
                    }
                    map1.put(s1, map1.getOrDefault(s1, 0) + 1);
                }
                if (map.equals(map1)) {
                    ans.add(i);
                }
            }
            return ans;
        }
    }

复杂度分析 时间复杂度: O((n-m)*m) n为s中长度 m为word中全部字符串拼接后的长度 空间复杂度:O(n)为哈希表所需要的大小

charlestang commented 2 years ago

思路

用一个滑动窗口,窗口宽度是 words 列表里所有单词连接在一起的总长度。然后,遍历滑动窗口的所有位置,检查子串能不能按照 words 给定的列表进行分割。如果可以分割,就找到了一个起始位置。

滑动窗口总共有 n - m c + 1 个,n 是 s 的长度,m 是 words 里单词的长度, c 是words 的单词个数。 每次创建哈希来统计字母频率,复杂度是 O(mc) 每次尝试分割子串,复杂度是 O(c)

总时间复杂度是O((n - m c + 1) m c),空间复杂度是 O(mc )

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        m = len(words[0])
        c =len(words)
        n = len(s)
        ans = []

        w = Counter(words)

        for i in range(n - m * c + 1):
            d = Counter()
            for j in range(c):
                word = s[i + j * m: i + j * m + m]
                if word not in w:
                    break
                d[word] += 1
                if d[word] > w[word]:
                    break
            else:
                ans.append(i)
        return ans
xiao-xiao-meng-xiang-jia commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { List 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 < s.length() - all_len + 1; i++) { String tmp = s.substring(i, i + all_len); HashMap<String, Integer> tmp_map = new HashMap<>(); for (int j = 0; j < all_len; j += one_word) { String w = tmp.substring(j, j + one_word); tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1); } if (map.equals(tmp_map)) res.add(i); } return res; } }

YuyingLiu2021 commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:

        #先把给出的word按不同的组合排列 放到一个新的set中
        #把每一个结果和s对比 如果符合就append 再标注首字母index 不符合就continue
        # 投机取巧的办法 面试的时候可以这么解吗
        import itertools
        per = itertools.permutations(words)
        words_set = set()
        for x in per:
            #words_set.add(x)
            words_set.add(''.join(x))

        res = []
        l_sub = len(words[0])*len(words)
        for i in range(len(s) - l_sub + 1):
            substring = s[i:i+l_sub]
            if substring in words_set:
                res.append(i)

        return res
1149004121 commented 2 years ago

30. 串联所有单词的子串

思路

①哈希表+遍历
由于words里的word有不同的组合顺序,考虑用哈希表保存,然后对s进行遍历,判断[i, i + word.length * words.length)里是否与哈希表中的匹配,可以将该子串按word.length分割,然后存进另一个哈希表中,比较两个哈希表是否相同。
② 哈希表+滑动窗口
由于每次遍历的时候都要把第二个哈希表清空,这样浪费了时间,因为从0开始的和从word.length的开始的第二个哈希表可能会存在公用的一部分。因此考虑[0, word.length)不同的起点,滑动窗口。

代码


    var findSubstring = function (s, words) {
      let hash1 = new Map();
      let res = [];
      for (const word of words) {
        hash1.set(word, (hash1.get(word) || 0) + 1);
      };
      const len = s.length;
      const wLen = words[0].length;
      const wsLen = words.length;
      for (let i = 0; i < len - wLen * wsLen + 1; i++) {
        let cur = s.substring(i, i + wLen * wsLen);
        let hash2 = new Map();
        var j = 0;
        for (; j < cur.length; j += wLen) {
          let string = cur.substring(j, j + wLen);
          hash2.set(string, (hash2.get(string) || 0) + 1);
          if (!hash1.has(string) || hash1.get(string) < hash2.get(string)) break;
        };
        if (j === cur.length) res.push(i);
      };
      return res;
    };


    var findSubstring = function (s, words) {
        let hash1 = new Map();
        let res = [];
        for (const word of words) {
            hash1.set(word, (hash1.get(word) || 0) + 1);
        };
        let n = s.length;
        let word_len = words[0].length;
        let words_len = words.length;

        for(let i = 0; i < word_len; i++){
            let left = i, right = i;
            let count = 0;
            let hash2 = new Map();
            while(right + word_len <= n){
                let newWord = s.substring(right, right + word_len);
                right += word_len;
                if(hash1.has(newWord)){
                    hash2.set(newWord, (hash2.get(newWord) || 0) + 1);
                    count++;
                    while(hash2.get(newWord) > hash1.get(newWord)){
                        let oldWord = s.substring(left, left + word_len);
                        hash2.set(oldWord, hash2.get(oldWord) - 1);
                        left += word_len;
                        count--;
                    }
                }else{
                    left = right;
                    hash2.clear();
                    count = 0;
                };
                if(count === words_len) res.push(left);
            }
        };
        return res;
    };

复杂度分析

Tesla-1i commented 2 years ago
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
babyyre 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]) word_num = len(words) n = len(s) words = Counter(words) res = [] for i in range(0, one_word): cur_cnt = 0 left = i right = i cur_Counter = Counter() while right + one_word <= n: w = s[right:right + one_word] right += one_word cur_Counter[w] += 1 cur_cnt += 1 while cur_Counter[w] > words[w]: left_w = s[left:left+one_word] left += one_word cur_Counter[left_w] -= 1 cur_cnt -= 1 if cur_cnt == word_num : res.append(left) return res

weiruizhang commented 2 years ago
/**
 * @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;
}
junbuer commented 2 years ago

思路

代码

from collections import Counter, defaultdict
class Solution(object):
    def findSubstring(self, s, words):
        """
        :type s: str
        :type words: List[str]
        :rtype: List[int]
        """
        cnt_words = Counter(words)
        d = len(words[0])
        l = d * len(words)
        n = len(s)
        ans = []
        for i in range(0, n - l + 1, d):
            for start in range(i, i + d):
                if start + l <= n:
                    flag = True
                    windows = s[start:start + l]
                    cnt_s = defaultdict(int) 
                    for j in range(0, l - d + 1, d):
                        ch = windows[j:j + d]
                        if ch in cnt_words:
                            cnt_s[ch] += 1
                            if cnt_s[ch] > cnt_words[ch]:
                                flag = False
                                break   
                        else:
                            flag = False
                            break
                    if flag:
                        ans.append(start)
        return ans   

复杂度分析

zhiyuanpeng commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        wh = len(words[0])
        ans = []
        # corner case
        if len(s) < wh:
            return ans
        #
        l = 0
        # iterate the s
        if len(s) == wh:
            if len(words) != 1:
                return []
            if words[0] not in s:
                return []
            else:
                return [0]
        for i in range(wh, len(s)):
            if s[l:i] in words:
                allw = words.copy()
                wl, wr = l, i
                for wr in range(i, len(s)+1, wh):
                    if s[wl: wr] in allw:
                        allw.remove(s[wl: wr])
                    else:
                        break
                    #
                    if not allw:
                        ans.append(l)
                        break
                    wl += wh
            l += 1
        return ans
Yrtryannn 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 < s.length() - all_len + 1; i++) {
            String tmp = s.substring(i, i + all_len);
            HashMap<String, Integer> tmp_map = new HashMap<>();
            for (int j = 0; j < all_len; j += one_word) {
                String w = tmp.substring(j, j + one_word);
                tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
            }
            if (map.equals(tmp_map)) res.add(i);
        }
        return res;
    }
}
Today-fine commented 2 years ago

抄答案学习大佬代码,大家看到请忽视我


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

        Map<String, Integer> wordCounter = new HashMap<>();
        for (String word : words) {
            wordCounter.put(word, wordCounter.getOrDefault(word, 0) + 1);
        }

        int wordLength = words[0].length(), lengthOfs = s.length(), wordCount = words.length;
        int wordsLength = wordLength * wordCount;

        for (int i = 0; i < wordLength; i++) {
            int left = i, right = i + wordLength, cnt = 0;
            Map<String, Integer> currCount = new HashMap<>();
            while (right <= lengthOfs) {
                // 统计字符串 s 的字串中含有 words 字符串的数量
                String currWord = s.substring(right - wordLength, right);
                currCount.put(currWord, currCount.getOrDefault(currWord, 0) + 1);
                cnt++;
                right += wordLength;

                while (currCount.getOrDefault(currWord, 0) > wordCounter.getOrDefault(currWord, 0)) {
                    String deleteWord = s.substring(left, left + wordLength);
                    currCount.put(deleteWord, currCount.getOrDefault(deleteWord, 0) - 1);
                    left += wordLength;
                    cnt--;
                }

                if (cnt == wordCount) {
                    res.add(left);
                }

            }
        }

        return res;
    }

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

Davont commented 2 years ago

code

var findSubstring = function(s, words) {
  const len = words[0].length
  const res = []
  // 循环, 循环的次数为字符串长度减去words所有字符串总和长度-1, 因为这个长度后面的字符串肯定不能拼接成words数组的数据
  for (let i = 0; i <= s.length - words.length * len; i++) {
    const wordsCopy = [...words]
    // 深度优先遍历
    dfs(wordsCopy, s.substring(i), i)
  }

  return res

  function dfs(arr, s, start) {
    // 递归的结束条件为数组的长度为0, 或者进不去下方的判断
    if (arr.length === 0) return res.push(start)

    // 从字符串开始剪切固定长度字符串, 去words中查找, 如果找不到, 结束, 如果找到了 继续往下查找
    const str = s.substr(0, len)

    const index = arr.findIndex((item) => item === str)

    if (index > -1) {
      // 递归查找之前需要将已经使用过的数组索引删除, 字符串也需要删除已经判断过的
      arr.splice(index, 1)
      dfs(arr, s.substring(len), start)
    }
  }
}
732837590 commented 2 years ago

学习一下他人的代码 public List findSubstring(String s, String[] words) { List res = new ArrayList(); int wordNum = words.length; if (wordNum == 0) { return res; } int wordLen = words[0].length(); //HashMap1 存所有单词 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++) { //HashMap2 存当前扫描的字符串含有的单词 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); //判断该单词在 HashMap1 中 if (allWords.containsKey(word)) { int value = hasWords.getOrDefault(word, 0); hasWords.put(word, value + 1); //判断当前单词的 value 和 HashMap1 中该单词的 value if (hasWords.get(word) > allWords.get(word)) { break; } } else { break; } num++; } //判断是不是所有的单词都符合条件 if (num == wordNum) { res.add(i); } } return res; }

shamworld commented 2 years ago
var findSubstring = function(s, words) {
    const len = words[0].length
  const res = []
  // 循环, 循环的次数为字符串长度减去words所有字符串总和长度-1, 因为这个长度后面的字符串肯定不能拼接成words数组的数据
  for (let i = 0; i <= s.length - words.length * len; i++) {
    const wordsCopy = [...words]
    // 深度优先遍历
    dfs(wordsCopy, s.substring(i), i)
  }

  return res

  function dfs(arr, s, start) {
    // 递归的结束条件为数组的长度为0, 或者进不去下方的判断
    if (arr.length === 0) return res.push(start)

    // 从字符串开始剪切固定长度字符串, 去words中查找, 如果找不到, 结束, 如果找到了 继续往下查找
    const str = s.substr(0, len)

    const index = arr.findIndex((item) => item === str)

    if (index > -1) {
      // 递归查找之前需要将已经使用过的数组索引删除, 字符串也需要删除已经判断过的
      arr.splice(index, 1)
      dfs(arr, s.substring(len), start)
    }
  }
};
tian-pengfei commented 2 years ago

时间很复杂 空间也很大 刚好通过了

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> re;

        //两个map相等便可以。

//        1.先构建expected map
        map<string,int> expect_map;
        for(auto const & word:words){
            expect_map[word]++;
        }
        int word_length = words[0].length();
        int start =0 ; int end =word_length*words.size();
        while (end<=s.size()){
            if(is_equal(s,start,end,word_length,expect_map))re.push_back(start);
            end++;
            start++;
        }

        return re;

    }

//        2. 判断是否相等
    bool is_equal(string &s,int start,int end,int word_length,map<string,int> ex_map){
        int pos =start;
         while(pos!=end){

            string key = s.substr(pos,word_length);
            if(ex_map.find(key)==ex_map.end())return false;
             ex_map[key]--;
             if(ex_map.at(key)==0)ex_map.erase(key);
            pos+=word_length;
        }
        return ex_map.empty();

    }
};
kbfx1234 commented 2 years ago

30. 串联所有单词的子串

// 1-3
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty()) return {};
        int len = s.size();
        unordered_map<string, int> mp;
        int a = 0;
        vector<int> ans;
        for (auto w : words) {
            ++mp[w];
        }
        int wlen = words[0].size(), count = words.size();
        int match = 0;
        for (int i = 0; i < len - wlen * count + 1; i++) {
            string cur = s.substr(i, wlen * count);
            unordered_map<string, int> temp;
            int j = 0, cnt = 0;
            for (; j < cur.size(); j += wlen) {
                string curword = cur.substr(j, wlen);

                if (mp.count(curword) == 0) break;
                temp[curword]++;
                cnt++;
                if (temp[curword] > mp[curword]) break;

                if (cnt == count) ans.push_back(i);
            }
        }
        return ans;
    }
};
baddate commented 2 years ago

题目

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

思路

使用哈希表记录 words 里面元素的个数,判断第一个字母开始的窗口是否存在于words,匹配则向后移动,重复操作。

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans={};
        int len=words[0].size();
        if(len>s.size()) return ans;
        unordered_map<string,int> mymap;
        for(int i=0;i<words.size();i++)
            mymap[words[i]]++;

        for(int i=0;i<=s.size()-len;i++){
            if(mymap.find(s.substr(i,len))==mymap.end()) continue;
            unordered_map<string,int> tmp=mymap;
            int j=i;
            for(int count=words.size();count>0;count--){
                string a=s.substr(j,len);
                if(tmp.find(a)==tmp.end() || tmp[a]==0) break; 
                else{
                    j+=len;
                    tmp[a]--;
                }            
                if(j==i+len*words.size()) ans.push_back(i);                
            }
        }
        return ans;
    }
};

复杂度分析

ZJP1483469269 commented 2 years ago
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
    List<Integer> res = new LinkedList<>();

    int len = words[0].length();
    for(int i=0;i<=s.length()-len*words.length;i++){
        HashMap<String,Integer> map = new HashMap<>();
        for(int k =0;k<words.length;k++){
            map.put(words[k],map.getOrDefault(words[k],0)+1);
        }
        int x=0;
        for(int j=0;j<words.length;j++){
            String cur_str = s.substring(i+j*len,i+(j+1)*len);
            if(map.containsKey(cur_str)){
                if(map.getOrDefault(cur_str,0)!=0) x++;
                map.put(cur_str,map.getOrDefault(cur_str,0)-1);
            }else{
                break;
            }
        }
        if(x==words.length){
            res.add(i);
        }

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

思路

两个HashMap来处理。

代码

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();
    //HashMap1 存所有单词
    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++) {
        //HashMap2 存当前扫描的字符串含有的单词
        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);
            //判断该单词在 HashMap1 中
            if (allWords.containsKey(word)) {
                int value = hasWords.getOrDefault(word, 0);
                hasWords.put(word, value + 1);
                //判断当前单词的 value 和 HashMap1 中该单词的 value
                if (hasWords.get(word) > allWords.get(word)) {
                    break;
                }
            } else {
                break;
            }
            num++;
        }
        //判断是不是所有的单词都符合条件
        if (num == wordNum) {
            res.add(i);
        }
    }
    return res;
}

复杂度分析

时间复杂度: O(N*m) m为word数组 空间复杂度:O(m)

Macvh commented 2 years ago

class Solution(object): def findSubstring(self, s, words): """ :type s: str :type words: List[str] :rtype: List[int] """ from collections import Counter if not s or not words:return [] all_len = sum(map(len, words)) n = len(s) words = Counter(words) res = [] for i in range(0, n - all_len + 1): tmp = s[i:i+all_len] flag = True for key in words: if words[key] != tmp.count(key): flag = False break if flag:res.append(i) return res

lilililisa1998 commented 2 years ago

class Solution(object): def findSubstring(self, s, words): """ :type s: str :type words: List[str] :rtype: List[int] """ from collections import Counter if not s or not words:return [] all_len = sum(map(len, words)) n = len(s) words = Counter(words) res = [] for i in range(0, n - all_len + 1): tmp = s[i:i+all_len] flag = True for key in words: if words[key] != tmp.count(key): flag = False break if flag:res.append(i) return res

dahaiyidi commented 2 years ago

Problem

30. 串联所有单词的子串

++

难度困难585

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

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


Note


Complexity


Python

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []
        wordmap = {}
        smap = {}
        for word in words:
            wordmap[word] = wordmap.get(word, 0) + 1

        wordlen = len(words[0])
        wordnum = len(words)
        res = []
        for k in range(wordlen):
            i = k
            j = k
            # 以s[i]开头的子串是否符合?
            while i <= len(s) - wordlen * wordnum:

                # 获取长度为wordlen * wordnum的子串
                while(j < i + wordlen * wordnum):
                    # 当前长度为wordlen的子串
                    tmp = s[j: j + wordlen]
                    smap[tmp] = smap.get(tmp, 0) + 1
                    j += wordlen

                    # 获取失败:刚获取的tmp在wordmap不存在,重置i,restart
                    if wordmap.get(tmp, 0) == 0:
                        i = j
                        smap.clear()
                        break
                    elif smap[tmp] > wordmap[tmp]:
                        # 获取失败,tmp 的数量超出wordmap中的数量,则需要不停地删除,直到数值不在高于wordmap中的数值
                        # 并重置i,restart
                        while smap[tmp] > wordmap[tmp]:
                            smap[s[i: i + wordlen]] -= 1
                            i += wordlen
                        break

                #  至此,已经获取长度为wordlen * wordnum的子串,或长度不足
                if j == i + wordlen * wordnum:
                    res.append(i) # 将当前i添加到res
                    # 删除第一个长度为wordlen的子串, 并开始下一个i
                    smap[s[i: i + wordlen]] -= 1
                    i += wordlen #

            smap.clear()
        return res

C++

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if(words.empty()) return {};
        vector<int> ans;
        unordered_map<string, int> wordmap, smap;
        for(string word: words){
            wordmap[word]++;
        } 
        int wordnum = words.size();
        int wordlen = words[0].size();
        for(int k = 0; k < wordlen; k++){
            int i = k, j = k;
            while(i < (s.size() - wordlen * wordnum + 1)){
                while(j < i + wordlen * wordnum){
                    string tmp = s.substr(j, wordlen);
                    smap[tmp]++;
                    j += wordlen;

                    // 不存在wordmap
                    if(wordmap[tmp] == 0){
                        i = j;
                        smap.clear();
                        break;
                    }
                    else if(smap[tmp] > wordmap[tmp]){
                        while(smap[tmp] > wordmap[tmp]){
                            smap[s.substr(i, wordlen)]--;
                            i += wordlen;
                        }
                        break;
                    }
                }

                if(j == i + wordlen * wordnum){
                    ans.push_back(i);
                    smap[s.substr(i, wordlen)]--;
                    i += wordlen;
                }
            }
            smap.clear();
        }
        return ans;

    }
};

From : https://github.com/dahaiyidi/awsome-leetcode

liuzekuan commented 2 years ago

思路

外层遍历字符串,内层遍历从当前位置到总单词长度的位置,匹配该字符串是否符合

代码

Java Code:


class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<Integer>();
        if(s.length()<=0||words.length<=0) {
            return res;
        }
        //定义一个map,存单词和对应的次数
        Map<String,Integer> words_map = new HashMap<String,Integer>();
        for(String str : words) {
            if(words_map.containsKey(str)) {
                words_map.put(str, words_map.get(str)+1);
            }else {
                words_map.put(str, 1);
            }
        }
        int wordLen=words[0].length();
        int wordStrLen=wordLen*words.length; //字符串总体长度
        for(int i=0;i<s.length()-wordStrLen+1;i++) {
            String tmpStr=s.substring(i,i+wordStrLen);
            Map<String,Integer> wordsTmpMap = new HashMap<String,Integer>(words_map);
            for(int j=0;j<wordStrLen;j+=wordLen) {
                String word=tmpStr.substring(j,j+wordLen);
                if(wordsTmpMap.containsKey(word)){
                    wordsTmpMap.put(word, wordsTmpMap.get(word)-1);
                    if(wordsTmpMap.get(word)==0) {
                        wordsTmpMap.remove(word);
                    }
                }else {
                    break;
                }
            }
            if(wordsTmpMap.size()==0){
                res.add(i);
            }
        }
        return res; 
    }
}

复杂度分析

令 n 为数组长度。

ray-hr 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 []
        all_len = sum(map(len, words))
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, n - all_len + 1):
            tmp = s[i:i+all_len]
            flag = True
            for key in words:
                if words[key] != tmp.count(key):
                    flag = False
                    break
            if flag:res.append(i)
        return res
Zhang6260 commented 2 years ago

JAVA版本

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

        HashMap<String,Integer> map=new HashMap<>();
        for (String temp:words){
            map.put(temp,map.getOrDefault(temp,0)+1);
        }
        for(int i=0;i+size<=s.length();i+=1){
            if(!map.containsKey(s.substring(i,i+size)))continue;
            int j=i;
            HashMap<String,Integer> map_t=new HashMap<>(map);
            while (j+size<=s.length()&&!map_t.isEmpty()){
                String temp=s.substring(j,j+size);
                if(!map_t.containsKey(temp)){
                    break;
                }else{
                    int t=map_t.get(temp);
                    if(t==1){
                        map_t.remove(temp);
                    }else{
                        map_t.put(temp,t-1);
                    }
                }
                j+=size;
            }
            if(map_t.isEmpty()){
                list.add(i);
            }
        }
        return list;

    }
}

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

空间复杂度:O(n)

tangjy149 commented 2 years ago

思路

用hash先记录每个单词出现次数,再遍历字符串,每次搜索单词,由于所有单词相同长度,因此可以substr截取相同长度字符串,得到单词。同时已知单词个数,那么就比对各个窗口的单词是否在hashtable中存在,若无,则整体向后移动,若有则检查下一个窗口,直到全部检查结束,将起始index进行记录

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans;
        unordered_map<string,int> helper;
        int wordsize=words[0].size();
        int totalsize=wordsize*words.size();
        if(wordsize>s.size()) return ans;
        for(auto it:words){
            helper[it]++;
        }
        for(int i=0;i<=s.size()-wordsize;i++){
            if(helper.find(s.substr(i,wordsize))!=helper.end()){
                unordered_map<string,int> temp=helper;
                int strat=i;
                // words.size()个窗口
                for(int j=0;j<words.size();j++){
                    string str=s.substr(strat,wordsize);
                    if(temp.find(str)==temp.end()||temp[str]==0) break;
                    strat+=wordsize;//查看下一个窗口
                    temp[str]--;
                }
                if(strat==i+totalsize){
                    ans.push_back(i);
                }
            }
        }
        return ans;
    }
};
Aobasyp commented 2 years ago

思路 从 words 入手,words 所有单词排列生成字符串 X, 通过字符串匹配查看 X 在 s 中的出现位置

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

}

时间复杂度 O(n∗m∗k)。

1916603886 commented 2 years ago

var findSubstring = function(s, words) { let left = 0,right = 0,wordsLen = words.length; if(wordsLen == 0) return []; let res = []; let gapLen = words[0].length; let needs = {}; let windows = {}; for(let i = 0;i < wordsLen;i++){ needs[words[i]] ? needs[words[i]]++ : needs[words[i]] = 1; } let needsLen = Object.keys(needs).length; let match = 0; for(let i = 0;i < gapLen;i++){ right = left = i; match = 0; while(right <= s.length - gapLen){ let c1 = s.substring(right,right + gapLen); right += gapLen; windows[c1] ? windows[c1]++ : windows[c1] = 1; if(windows[c1] === needs[c1]){ ++match; } while(left < right && match == needsLen){ if(Math.floor((right - left) / gapLen) == wordsLen){ res.push(left); } let c2 = s.substring(left,left + gapLen); left += gapLen; windows[c2]-- ; if(needs[c2] && windows[c2] < needs[c2]){ match--; } } } windows = {}; } return res; };

uniqlell commented 2 years ago

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        if (words == null) return null;
        Map<String,Integer> need = new HashMap<>();
        for (String word:words) need.put(word,need.get(word)==null?1:need.get(word)+1);
        int len = words[0].length();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            Map<String,Integer> window = new HashMap<>();
            int left=i,right=i,count=0;
            while (right <= s.length() - len) {
                String str = s.substring(right,right + len);
                right+=len;
                if (need.containsKey(str)) {
                    window.put(str,window.get(str)==null?1:window.get(str)+1);
                    if (need.get(str).equals(window.get(str))) count++;
                }
                // 如果窗口中的字符长度恰好等于所需要的字符长度 则判断是否有满足单词全匹配
                if ((right - left) / len == words.length) {
                    if (count == need.size()) list.add(left);
                    String str1 = s.substring(left,left + len);
                    left+=len;
                    if (need.containsKey(str1)) {
                        if (need.get(str1).equals(window.get(str1))) count--;
                        // 删除窗口中的字符串
                        window.put(str1,window.get(str1)-1);
                    }
                }
            }
        }
        return list;
    }
}
MissNanLan commented 2 years ago

代码

JavaScript Code:


var findSubstring = function (s, words) {
    const len = words[0].length
    const res = []
    for (let i = 0; i <= s.length - words.length * len; i++) {
      const wordsCopy = [...words]
      dfs(wordsCopy, s.substring(i), i)
    }
    return res
    function dfs(arr, s, start) {
      if (arr.length === 0) return res.push(start)
      const str = s.substr(0, len)
      const index = arr.findIndex((item) => item === str)
      if (index > -1) {
        arr.splice(index, 1)
        console.log(arr.splice(index, 1))
        dfs(arr, s.substring(len), start)
      }
    }
}
Hacker90 commented 2 years ago

思路

用了一种滑动窗口的方法

因为单词都是定长的,所以本质上和单个字符一样。

比如s = "a1b2c3a1d4",words=["a1", "b2", "c3", "d4"]

窗口最开始为空:

a1在集合中,加入窗口 【a1】b2c3a1d4

b2在集合中,加入窗口 【a1b2】c3a1d4

c3在集合中,加入窗口 【a1b2c3】a1d4

a1在集合中,但前面a1已经出现了一次,达到了次数上限,不能再加入窗口。此时只需要把窗口向右移动一个单词:a1【b2c3a1】d4

d4在集合中,加入窗口a1【b2c3a1d4】此时找到了一个匹配。


class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int word_len = words[0].size();
        int len = words.size()*word_len;
        int n = s.size();
        unordered_map<string, int> m;              // <单词,出现的次数>
        for(int i=0; i<words.size(); ++i)
        {
            if(m.count(words[i]) == 0)             // 注意count方法的使用
                m.insert(make_pair(words[i], 1));
            else
                ++m[words[i]];
        }

        vector<int> index;
        for(int i=0; i<word_len; ++i)
        {
            unordered_map<string, int> win;
            int left = i;                           // 窗口左边沿
            int count = 0;                          // 窗口中的单词数目
            for(int right=i; right<=n-word_len; right+=word_len)
            {
                string word = s.substr(right, word_len);
                if(m.find(word) != m.end())         // 在集合中
                {
                    if(win.find(word) == win.end()) // 不在窗口中,即添加
                        win[word] = 1;
                    else
                        ++win[word];                // 在窗口中

                    if(win[word] <= m[word])        // 还没出现相应的次数
                        ++count;
                    else  
                    {
                        // 单词在集合中,但是它已经在窗口中出现了相应的次数,不应该加入窗口
                        // 此时把窗口起始位置想左移动到,该单词第一次出现的位置的下一个单词位置
                        for(int k=left;  ; k+=word_len)
                        {
                            string temp = s.substr(k, word_len);
                            --win[temp];
                            if(temp == word)
                            {
                                left = k + word_len;
                                break;
                            }
                            --count;
                        }
                    }

                    if(count == words.size())
                        index.push_back(left);
                }
                else                                // 不在集合中,从窗口右侧重新开始
                {
                    left = right + word_len;
                    win.clear();
                    count = 0;
                }
            }
        }

        return index;
    }
};

复杂度分析

moirobinzhang commented 2 years ago

Code:

public class Solution { public IList FindSubstring(string s, string[] words) { IList result = new List();

    if (words == null || words.Length == 0)
        return result;

    Dictionary<string, int> strDict = new Dictionary<string, int>();
    foreach(string word in words)
    {
        if (!strDict.ContainsKey(word))
            strDict.Add(word, 0);

        strDict[word]++;            
    }

    int strLen = s.Length;
    int wordLen = words[0].Length;
    int count = words.Length;
    int wordsLen = wordLen * count;

    if (strLen < wordsLen)
        return result;

    for ( int i = 0; i < strLen - wordsLen + 1; i++)
    {
        string currentStr = s.Substring(i, wordsLen);
        Dictionary<string, int> currentDict = new Dictionary<string, int>();
        int currentIndex = 0;

        while ( currentIndex < currentStr.Length)
        {
            string currentWord = currentStr.Substring(currentIndex, wordLen);

            if (!strDict.ContainsKey(currentWord))
                break;

            if (!currentDict.ContainsKey(currentWord))
                currentDict.Add(currentWord, 0);

            currentDict[currentWord]++;  

            if (currentDict[currentWord] > strDict[currentWord])
                break;

            currentIndex += wordLen;
        }

        if (currentIndex == currentStr.Length)
            result.Add(i);

    }

    return result;        
}

}

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

复杂度分析

pwqgithub-cqu commented 2 years ago
vector<int> findSubstring(string s, vector<string>& words) {
    if(words.empty()) return {};
    unordered_map<string,int> wordmap,smap;
    for(string word:words) wordmap[word]++;
    int wordlen = words[0].size();
    int wordnum = words.size();
    vector<int> ans;
    for(int k=0;k<wordlen;k++){
        int i=k,j=k;
        while(i<s.size()-wordnum*wordlen+1){
            while(j<i+wordnum*wordlen){
                string temp = s.substr(j,wordlen);
                smap[temp]++;
                j+=wordlen;
                if(wordmap[temp]==0){//情况二,有words中不存在的单词
                    i=j;//对i加速
                    smap.clear();
                    break;
                }
                else if(smap[temp]>wordmap[temp]){//情况三,子串中temp数量超了
                    while(smap[temp]>wordmap[temp]){
                        smap[s.substr(i,wordlen)]--;
                        i+=wordlen;//对i加速
                    }
                    break;
                }                   
            }
            //正确匹配,由于情况二和三都对i加速了,不可能满足此条件
            if(j==i+wordlen*wordnum){
                ans.push_back(i);
                smap[s.substr(i,wordlen)]--;
                i+=wordlen;//i正常前进
            }
        }
        smap.clear();
    }
    return ans;
}
alongchong 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 < s.length() - all_len + 1; i++) {
            String tmp = s.substring(i, i + all_len);
            HashMap<String, Integer> tmp_map = new HashMap<>();
            for (int j = 0; j < all_len; j += one_word) {
                String w = tmp.substring(j, j + one_word);
                tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
            }
            if (map.equals(tmp_map)) res.add(i);
        }
        return res;
    }
}
callmeerika commented 2 years ago

思路

每当匹配到一个数组内的字符串的时候,就检验后续的字符串是否满足要求,满足则记录第一个位置,不满足则继续

代码

var findSubstring = function(s, words) {
    if(!s || !words.length) return [];
    let singleLength = words[0].length;
    let totalLength = singleLength * words.length;
    let arr = [];
    let obj = {};
    words.forEach(vv => {
        obj[vv] = (obj[vv] || 0) + 1;
    })
    for(let i = 0; i < s.length; i++) {
        let str = s.slice(i,i + singleLength);
        if(obj[str]) {
            let temp = Object.assign({},obj);
            for(let j = i; j < i + totalLength;j = j + singleLength) {
                let tempStr = s.slice(j,j+ singleLength);
                if(temp[tempStr]) {
                    temp[tempStr] = temp[tempStr] - 1;
                } else {
                    break;
                }
            }
            if(Object.values(temp).every(vv=>vv===0)) {
                arr.push(i);
            }
        }
    }
    return arr;
};

复杂度

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

15691894985 commented 2 years ago

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

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

思路:

Okkband 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 []
        all_len = sum(map(len, words))
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, n - all_len + 1):
            tmp = s[i:i+all_len]
            flag = True
            for key in words:
                if words[key] != tmp.count(key):
                    flag = False
                    break
            if flag:res.append(i)
        return res
rootdavid commented 2 years ago
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
    int n = s.size(), m = words.size(), w = words[0].size();
      vector<int> res;

      unordered_map<string, int> tot;

      for (auto& word : words) tot[word]++;

      if (words.empty()) return res;

      for (int i = 0; i < w; i++) {
        int cnt = 0;
        unordered_map<string, int> wd;

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

            if (wd[word] < tot[word]) cnt--;

          }
          auto word = s.substr(j, w);

          wd[word]++;

          if (wd[word] <= tot[word]) cnt++;

          if (cnt == m) res.push_back(j - (m - 1) * w);
        }

      }

      return res;

    }
};
Jay214 commented 2 years ago

思路

与官方题解相似,遍历 s 串中所有长度为 (words[0].length * words.length) 的子串 W,查看 W是否可以由 words 数组构造生成,用两个map做判断

代码

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    const all_len = words[0].length * words.length
    const w_len = words.length
    const ws = words[0].length
    const num_map = new Map()
    const res = []
    words.forEach(v => {
        num_map.set(v, (num_map.get(v) || 0) + 1)
    })
    const map = new Map()
    for (let i = 0; i < s.length - all_len + 1; i++) {
        const str = s.substr(i, all_len)
        let step = 0
        let num = 0
        map.clear()
        while (num < w_len) {
            const ss = str.substr(step, ws)
            if (!num_map.has(ss) || ((map.get(ss) || 0) >= num_map.get(ss))) {
                break
            } else {
                map.set(ss, (map.get(ss) || 0) + 1)
                num += 1
                step += ws
            }
        }
        if (num === w_len) {
            res.push(i)
        }
    }
    return res
};

复杂度

令 n 为字符串 S 长度, m 为 words 数组元素个数, k 为单个 word 字串长度。

时间复杂度: 本质上我们的算法是将 s 划分为若干了段,这些段的长度为 m km∗k,对于每一段我们最多需要检查 n - m kn−m∗k 次,因此时间复杂度为 O(n m k)O(n∗m∗k)。

空间复杂度: temp 在下一次循环会覆盖上一次的 temp,因此 temp 的空间在任意时刻都不大于 O(m)O(m), 因此空间复杂度为 O(m)O(m)。

Alfie100 commented 2 years ago

解题思路

多起点滑动窗口+哈希表+哈希表

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:

        num_word = len(words)
        word_len = len(words[0])
        total_word_len = num_word * word_len
        cnt = Counter(words)

        res = []
        n = len(s)
        for i in range(word_len):   # world_len 个滑动窗口
            left = right = i        # 窗口的左右边界
            cur_cnt = Counter()     # 当前窗口中的word哈希表
            cur_num_word = 0

            while right + word_len <= n:
                word = s[right:right+word_len]      # 窗口中新出现的词 word
                right += word_len       # 窗口继续往右滑动

                # 【保证新出现的word在words中】间接保证当前窗口中的word均在words里,只是出现次数待计算
                # if word not in cnt:
                if cnt[word] == 0:     # 窗口中新出现的word未在words中,此时直接将left移动到right处,当前窗口大小变为0,不包含任何词
                    left = right
                    cur_cnt.clear()     # 清空当前窗口统计量【当前窗口大小为0】
                    cur_num_word = 0
                    continue

                # 否则,若word存在与words里,则统计word信息,并根据情况移动left
                cur_cnt[word] += 1      # word的统计量+1
                cur_num_word += 1

                # 新出现的word导致当前窗口中word的统计量超限,left需往右滑动,直至统计量达标
                while cur_cnt[word] > cnt[word]:
                    left_word = s[left: left+word_len]
                    cur_cnt[left_word] -= 1
                    cur_num_word -= 1
                    left += word_len

                if cur_num_word == num_word:
                    res.append(left)

        return res

复杂度分析

WANGDI-1291 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 []
        all_len = sum(map(len, words))
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, n - all_len + 1):
            tmp = s[i:i+all_len]
            flag = True
            for key in words:
                if words[key] != tmp.count(key):
                    flag = False
                    break
            if flag:res.append(i)
        return res
zzzpppy commented 2 years ago

看不懂,太菜了,直接贴题解了

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

        int n = s.length(), m = words.length, w = words[0].length();

        // 统计 words 中「每个目标单词」的出现次数
        Map<String, Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }

        for (int i = 0; i < w; i++) {
            // 构建一个当前子串对应 map,统计当前子串中「每个目标单词」的出现次数
            Map<String, Integer> curMap = new HashMap<>();
            // 滑动窗口的大小固定是 m * w
            // 每次将下一个单词添加进 cur,上一个单词移出 cur
            for (int j = i; j + w <= n; j += w) {   
                String cur = s.substring(j, j + w);
                if (j >= i + (m * w)) {
                    int idx = j - m * w;
                    String prev = s.substring(idx, idx + w);
                    if (curMap.get(prev) == 1) {
                        curMap.remove(prev);
                    } else {
                        curMap.put(prev, curMap.get(prev) - 1);
                    }
                }
                curMap.put(cur, curMap.getOrDefault(cur, 0) + 1);
                // 如果当前子串对应 map 和 words 中对应的 map 相同,说明当前子串包含了「所有的目标单词」,将起始下标假如结果集
                if (map.containsKey(cur) && curMap.get(cur).equals(map.get(cur)) && cmp(map, curMap)) {
                    ans.add(j - (m - 1) * w);
                }
            }
        }

        return ans;
    }
    // 比较两个 map 是否相同
    boolean cmp(Map<String, Integer> m1, Map<String, Integer> m2) {
        if (m1.size() != m2.size()) return false;
        for (String k1 : m1.keySet()) {
            if (!m2.containsKey(k1) || !m1.get(k1).equals(m2.get(k1))) return false;
        }
        for (String k2 : m2.keySet()) {
            if (!m1.containsKey(k2) || !m1.get(k2).equals(m2.get(k2))) return false;
        }
        return true;
    }
}