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

shamworld commented 2 years ago

思路

注意点:

freedom0123 commented 2 years ago
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        Map<String, Integer> allWords = new HashMap<>();
        for(String word : words){
            allWords.put(word, allWords.getOrDefault(word, 0) + 1);
        }
        int wordNum = words.length, wordLen = words[0].length();
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < s.length() - wordNum * wordLen + 1; i++){
            Map<String, Integer> subWords = new HashMap<>();
            int index = i;
            while(index < i + wordNum * wordLen){
                String curWord = s.substring(index, index + wordLen);
                if(!allWords.containsKey(curWord) || subWords.get(curWord) == allWords.get(curWord)){
                    break;
                }
                subWords.put(curWord, subWords.getOrDefault(curWord, 0) + 1);
                index += wordLen;
            }
            if(index == i + wordNum * wordLen){
                res.add(i);
            }
        }
        return res;
    }
}
hewenyi666 commented 2 years ago

题目名称

30. 串联所有单词的子串

题目链接

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

题目思路

滑动窗口!

我们一直在 s 维护着所有单词长度总和的一个长度队列!

code for Python3

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

复杂度分析

ZhuMengCheng commented 2 years ago

思路: 暴力解法,使用map保存,循环判断每一个词是否符合 双层循环,(半天只看懂了这个..)

var findSubstring = function (s, words) {
    if (!words || !words.length) return [];
    let wordLen = words[0].length;
    // 计算所有字串的长度  因为内部字串的长度都相同.直接用一个字串的长度乘以语所有字串的数量
    let allWordsLen = wordLen * words.length;
    let ans = []
    // 统计字符出现的次数
    let wordMap = {};
    for (let w of words) {
        wordMap[w] ? wordMap[w]++ : wordMap[w] = 1
    }

    // 循环次数为总长度减去字符长度+1 因为再往后查找,肯定没有符合的字符
    for (let i = 0; i < s.length - allWordsLen + 1; i++) {

        let wm = Object.assign({}, wordMap);
        for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
            // 拆分字符.查看是否符合要求
            let w = s.slice(j, j + wordLen);
            if (wm[w]) {
                wm[w]--
            } else {
                break;
            }
        }
        if (Object.values(wm).every(n => n === 0)) ans.push(i);
    }
    return ans;
};

时间复杂度:O(NM) s字符长度N wordsM 空间复杂度:O(N)

Shinnost 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
liuyangqiQAQ commented 2 years ago
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        int n = words[0].length();
        int sumLength = n * words.length;
        Map<String, Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < s.length() - sumLength + 1; i++) {
            HashMap<String, Integer> tempMap = new HashMap<>();
            String cur = s.substring(i, i + sumLength);

            for (int j = 0; j < sumLength; j += n) {
                String s1 = cur.substring(j, j + n);
                if(!map.containsKey(s1)) {
                    break;
                }
                tempMap.put(s1, tempMap.getOrDefault(s1, 0) + 1);
                if(map.get(s1) < tempMap.get(s1)) {
                    break;
                }
                if(j + n == sumLength) {
                    res.add(i);
                }
            }
        }
        return res;
    }
}
JAYWX commented 2 years ago
from collections import Counter
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words: return []
        res = []
        n = len(words)
        word_len = len(words[0])
        window_len = word_len * n
        target = Counter(words)
        i = 0
        while i < len(s) - window_len + 1:
            sliced = []
            start = i
            for _ in range(n):
                sliced.append(s[start:start + word_len])
                start += word_len
            if Counter(sliced) == target:
                res.append(i)
            i += 1
        return res
Wu-zonglin commented 2 years ago

思路

哈希表、滑动窗口

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        words_length = len(words)*len(words[0])
        words_dict = collections.Counter(words)
        ans = []
        for i in range(0, len(s)-words_length+1):
            temp = s[i:i+words_length]
            temp_list = [temp[j:j+len(words[0])] for j in range(len(temp)) if j % len(words[0]) == 0]
            temp_dict = collections.Counter(temp_list)
            if words_dict == temp_dict:
                ans.append(i)
        return ans
jerry9926 commented 2 years ago

思路

(mark 先抄作业)

代码

const findSubstring = (s, words) => {
  if (!words || words.length === 0) return [];

  const m = words.length,
    n = words[0].length,
    len = m * n,
    result = [];

  // Build the word-count hash map
  const map = {};
  for (word of words) map[word] = ~~map[word] + 1;

  // Try every possible start position i
  for (let i = 0; i < s.length - len + 1; i++) {
    // Make a copy of the hash map
    const temp = Object.assign({}, map);

    for (let j = i; j < i + len; j += n) {
      const str = s.substr(j, n);
      // Cannot find the word in hash map (words list), try another position
      if (!(str in temp)) break;
      // All the same word str are found, remove it from the hash map
      if (--temp[str] === 0) delete temp[str];
    }

    // We have gone through the whole s and used all our words in the list
    if (Object.keys(temp).length === 0) result.push(i);
  }

  return result;
};

复杂度分析

ysy0707 commented 2 years ago

思路:哈希表+滑动窗口

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        HashMap<String,Integer> map1 = new HashMap<>();
        //定义单个word长度,个数,并且自然得到子串的长度
        int oneWord = words[0].length();
        int wordNum = words.length;
        int len = oneWord * wordNum; 
        for(String word :words){
            //存储单词各个word的次数: key为单词,value为单词出现次数
            map1.put(word,map1.getOrDefault(word,0) + 1);
        }
        for(int i = 0; i < s.length() - len + 1; i++){
            //循环截取所有串联字符串
            //substring(x, y):x, y分别为开始/结束位置,左闭右开,[ )
            String tmp = s.substring(i,i + len);
            HashMap<String,Integer> map2 = new HashMap<>();
            //所有给定长度的字符串,按照单个word长度拆分
            //截取字符串出现次数,key为单词,value为单词出现次数
            for(int j = 0; j < len; j += oneWord){
                String w = tmp.substring(j, j +oneWord);
                map2.put(w,map2.getOrDefault(w, 0) + 1);
            }
            if(map1.equals(map2)){
                res.add(i);
            }
        }
        return res;
    }
}

时间复杂度:O(MN) 空间复杂度:O(N)

Socrates2001 commented 2 years ago

C implementation

typedef struct myStruct {
    char *key;
    int cnt;     /* 单词表中key单词出现的次数 */
    int usedCnt; /* 子串中已经被使用的次数,由于顺序无所谓,所以只统计次数是否用完即可 */
    UT_hash_handle hh;
} WordNode;

WordNode *map;

void Init(char ** words, int wordsSize)  /* 创建单词的hash表 */
{
    map = NULL;
    WordNode *s = NULL;
    int len = strlen(words[0]);
    for (int i = 0; i < wordsSize; i++) {
        HASH_FIND_STR(map, words[i], s);
        if (s == NULL) {
            s = calloc(1, sizeof(WordNode));
            s->key = calloc(len + 1, sizeof(char));
            strcpy(s->key, words[i]);
            s->cnt++;
            HASH_ADD_KEYPTR(hh, map, s->key, len, s);
        } else {
            s->cnt++;
        }
    }
}
void ResetMap()
{
    WordNode *s, *tmp;
    HASH_ITER(hh, map, s, tmp) {
        s->usedCnt = 0;
    }
}
int* findSubstring(char * s, char ** words, int wordsSize, int* returnSize)
{
    if ((s == NULL) || (words == NULL) || (wordsSize == 0)) {
        *returnSize = 0;
        return NULL;
    }
    Init(words, wordsSize);
    int len = strlen(s);
    int *res = calloc(len, sizeof(int));
    int cnt = 0;
    int wordLen = strlen(words[0]);
    char *tmp = calloc(wordLen + 1, sizeof(char));
    WordNode *t;
    for (int i = 0; i <= len - (wordLen * wordsSize); i++) { /* 每个字母进行向后移位 */
        int j;
        ResetMap(); /* 重置map中的used计数 */
        for (j = i; j < i + (wordLen * wordsSize); j += wordLen) {  /* 当前从i开始,子串分割成wordSize个单词,查找hash表 */
            strncpy(tmp, &s[j], wordLen);
            HASH_FIND_STR(map, tmp, t);
            if ((t == NULL) || (t->usedCnt == t->cnt)) { /* 如果找不到,或者使用次数已经超过单词表中存在的数量,break */
                break;
            } else {
                t->usedCnt++;  /* 使用次数+1 */
            }
        }
        if (j >= i + (wordLen * wordsSize)) { /* 所有单词都能匹配上 */
            res[cnt++] = i;
        }
    }
    /* 这里需要用HASH_ITER释放hash表,提交时忘了,但也能过,懒得加上了 */
    *returnSize = cnt;
    return res;
}
nekomoon404 commented 2 years ago

【思路—哈希表+双指针】

vector<int> findSubstring(string s, vector<string>& words) {
    vector<int> ans;
    unordered_map<string, int> map;
    if(words.size() == 0) 
        return ans;

    for(auto word : words)
        map[word]++;

    int sLen = s.size(), wLen = words[0].size(), wCnt = words.size();
    int match = 0;

    for(int i = 0; i < sLen - wLen * wCnt + 1; i++){
        string cur = s.substr(i, wLen * wCnt);
        unordered_map<string, int> tmp_map;

        int j = 0;
        for(; j < cur.size(); j += wLen) {
            string word = cur.substr(j, wLen);
            if(map.find(word) == map.end())
                break;
            tmp_map[word]++;
            if(tmp_map[word] > map[word])
                break;
        }
        if(j == cur.size())
            ans.push_back(i);
    }
    return ans;
}

时间复杂度:$O(n\cdot m \cdot k)$,其中n 为字符串 S 长度, m 为 words 数组元素个数, k 为单个 word 字串长度;

空间复杂度:$O(m)$。

Cartie-ZhouMo commented 2 years ago

思路

遍历s,每次取words中单词串联形成的子串长度,比较子串中单词出现次数与words是否相同。

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        hashset = {}
        for w in words:
            hashset[w] = hashset.setdefault(w, 0) + 1
        l = len(words)
        wl = len(words[0])
        ans = []
        for i in range(len(s)-wl*l+1):
            tmp = {}
            for j in range(i, i+wl*l, wl):
                tmp[s[j:j+wl]] = tmp.setdefault(s[j:j+wl], 0) + 1
            if tmp == hashset:
                ans.append(i)
        return ans

复杂度

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法 1: 哈希表

思路

naive 的想法是,将 words 里面的单词组合成字符串,然后到 s 里面去找有没有跟它一样的子串。但是,这样子时间复杂度太高了,因为 words 的组合有 words.length! 种,总复杂度差不多就是 s.length * words.length! 了。

那我们反过来想,s 的子串最多只有 s.length 个,那我们只要判断 s 的每个子串是不是刚好由 words 数组里面的所有单词组成的就可以了。所以说,如果子串是刚好匹配 words 的单词的话,说明子串和 words 组成的字符串是两种组合情况,虽然排序不同,但包含的单词种类和数量是一样的。

所以解决方法就很明显了:

复杂度分析

代码

JavaScript Code

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    const wordLen = words[0].length;
    const substrLen = wordLen * words.length;
    const initialWordsMap = words.reduce((map, w) => {
        map[w] = (map[w] || 0) + 1;
        return map;
    }, {})
    const res = [];

    for (let i = 0; i <= s.length - substrLen; i++) {
        const wordsMap = {...initialWordsMap};
        for (let j = i; j < i + substrLen; j += wordLen) {
            const word = s.slice(j, j + wordLen);
            if (!(word in wordsMap) || wordsMap[word] == 0) break;
            wordsMap[word]--;
        }
        if (usedUpWords(wordsMap)) res.push(i);
    }
    return res;

    // ******************************************
    function usedUpWords(map) {
        return Object.values(map).every(n => n == 0);
    }
};
/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function (s, words) {
    const wordSize = words[0].length;
    const substringLen = wordSize * words.length;

    const wordsCount = {};
    words.forEach(w => (wordsCount[w] = (wordsCount[w] || 0) + 1));

    const res = [];
    for (let i = 0; i <= s.length - substringLen; i++) {
        const tempCount = { ...wordsCount };
        let count = words.length;

        for (let j = i; j < i + substringLen; j += wordSize) {
            const word = s.slice(j, j + wordSize);

            if (!(word in tempCount) || tempCount[word] <= 0) break;

            tempCount[word]--;
            count--;
        }

        if (count === 0) res.push(i);
    }
    return res;
};

更多题解可以访问:https://github.com/suukii/91-days-algorithm

JinMing-Gu commented 2 years ago
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ans={};
        //len将是我们后续“窗口”移动的步长
        int len=words[0].size();
        //若s的长度比“窗口”的长度还要短,输出{}
        if(len>s.size()) return ans;
        //使用哈希表记录words里面元素的个数,两个作用:1)判断words中是否存在某子串;2)判断words中某子串的个数
        unordered_map<string,int> mymap;
        for(int i=0;i<words.size();i++)
            mymap[words[i]]++;

        //遍历字符串s
        for(int i=0;i<=s.size()-len;i++){
            //若以i开头的子串不存在于哈希表中,也就无需后续比较,直接continue
            if(mymap.find(s.substr(i,len))==mymap.end()) continue;
            //若存在:
            //由于mymap和i后续还要用到,不能对其进行更改,故定义两个临时变量:哈希表tmp、整型j
            unordered_map<string,int> tmp=mymap;
            int j=i;
            //对每一个“窗口”进行判断,若该窗口子串存在且其个数大于0,将其个数减1,窗口向后移动一个步长,否则退出循环
            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]--;
                }      
                //若直到进行了 words.size() 个窗口的判断子串都存在,匹配成功,将 i 的值放入ans         
                if(j==i+len*words.size()) ans.push_back(i);                
            }
        }
        return ans;
    }
};
lxy030988 commented 2 years ago

思路

代码 js

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
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)
      dfs(arr, s.substring(len), start)
    }
  }
}

复杂度分析

Zhang6260 commented 2 years ago

JAVA版本

思路:(暴力法),从字符串当前位置进行遍历,判断能够构成words,若不构成则移动到下一个位置。

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*m)

空间复杂度:O(m)

Toms-BigData commented 2 years ago

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

思路

先mark一下,今天太忙,明儿再看

代码

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

思路

Hash表+双指针

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        word_len = len(words[0])
        words_length = len(words) * word_len
        ret = []
        if not s or not words or len(s) < words_length:
            return ret
        l = 0
        hash_words = Counter(words)
        while l <= len(s) - words_length:
            child_words = [s[i:i + word_len] for i in range(l, l + words_length, word_len)]
            if hash_words == Counter(child_words):
                ret.append(l)
            l += 1
        return ret

复杂度

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

MissNanLan commented 2 years ago

代码

JavaScript Code:


/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
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)
      dfs(arr, s.substring(len), start) 
    }
  }
}

复杂度分析

令 n 为数组长度。

joeytor commented 2 years ago

思路

先用哈希表统计 w 中每个单词的词频

然后每次取 word 中长度相同的一段, 分割成 num_word 的小段进行匹配

​ 如果 这个单词 不在哈希表中或者超出原来的数目, 那么就返回

​ 否则将这个 index 加入 result

代码

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

        d = collections.defaultdict(int)

        for w in words:
            d[w] += 1

        len_word = len(words[0])
        num_words = len(words)
        len_s = len(s)
        res = []

        for i in range(0, len_s - len_word * num_words+1):
            part_word = s[i:i+len_word * num_words]
            part_words = [part_word[x*len_word:(x+1)*len_word] for x in range(num_words)]

            d1 = collections.defaultdict(int)
            is_substring = True
            for w in part_words:
                if w not in d:
                    is_substring = False
                    break
                else:
                    d1[w] += 1
                    if d1[w] > d[w]:
                        is_substring = False
                        break
            if is_substring:
                res.append(i)

        return res

复杂度

N 为 s 长度, m 为 word 数组元素个数, n 为 word 数组单词长度

时间复杂度: O(Nm) 外面一层循环遍历 N 为 s 的长度, 里面的循环遍历个 里面的单词, 数目 为 m 个

空间复杂度: O(m) 哈希表的复杂度是 O(m)

zszs97 commented 2 years ago

开始刷题

题目简介

【Day 23】2021-10-2 - (30. 串联所有单词的子串)

题目思路

题目代码

代码块

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

复杂度

xbhog commented 2 years ago

思路:

不是很理解,先打打卡,正在啃,,,

Java代码段

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ret;
        if(words.size() == 0)//判断words为空,因为下面用到了words[0]
            return ret;

        int word_size = words[0].size();
        int word_num = words.size();

        unordered_map<string,int> m1;//构造hashmap
        for(int i=0;i<word_num;i++)
            m1[words[i]]++;

        unordered_map<string,int> m2;
        for(int i=0; (i + word_size * word_num) <= s.size() ; i++){//截取的s符合题意
            int j = 0;
            for(j=i;j < (i + word_size * word_num) ; j=j+word_size){//分段判断
                string temp_str = s.substr(j,word_size);
                if(m1[temp_str] == 0){//m1中没有截取的串,直接跳出
                    break;
                }else{
                    m2[temp_str]++;
                    if(m1[temp_str] < m2[temp_str])//重复次数过多,也跳出
                        break;
                }
            }

            if(j == (i + word_size * word_num))//每一段都符合,则加入答案
                ret.push_back(i);

            m2.clear();//清空m2
        }
        return ret;
    }
};

复杂度分析

时间复杂度:O(s.length()* words.length)

空间复杂度: O(words.length)

KennethAlgol commented 2 years ago

语言

java

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        Map<String, Integer> allWords = new HashMap<>();
        for(String word : words){
            allWords.put(word, allWords.getOrDefault(word, 0) + 1);
        }
        int wordNum = words.length, wordLen = words[0].length();
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < s.length() - wordNum * wordLen + 1; i++){
            Map<String, Integer> subWords = new HashMap<>();
            int index = i;
            while(index < i + wordNum * wordLen){
                String curWord = s.substring(index, index + wordLen);
                if(!allWords.containsKey(curWord) || subWords.get(curWord) == allWords.get(curWord)){
                    break;
                }
                subWords.put(curWord, subWords.getOrDefault(curWord, 0) + 1);
                index += wordLen;
            }
            if(index == i + wordNum * wordLen){
                res.add(i);
            }
        }
        return res;
    }
}
EggEggLiu commented 2 years ago

思路

先遍历一遍输入的words建立词频哈希。再遍历每个长度可能符合要求的子串,因为words中单词长度相同,所以在每个点截词去之前建立的词频哈希中比对,能完成一一对应的子串即为所求子串

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        map<string, int> mp;
        vector<int> res;
        int cnt = 0;
        for (string word : words) {
            ++mp[word];
            ++cnt;
        }
        int wordLen = words[0].size();
        int ss = wordLen * cnt;
        const size_t len = s.size();
        for (int i = 0; i < len - ss + 1; ++i) {
            string tmp = s.substr(i, ss);
            map<string, int> tmpMp(mp);
            bool flag = true;
            for (int index = 0; index < ss; index += wordLen) {
                if (!tmpMp[tmp.substr(index, wordLen)]) {
                    flag = false;
                    break;
                }
                --tmpMp[tmp.substr(index, wordLen)];
            }
            if (flag) {
                res.push_back(i);
            }
        }
        return res;
    }
};

复杂度

时间O(m)——m为s长度

空间O(n)——n为words.length

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

java

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

lihuiwen commented 2 years ago

代码

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

思路

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function (s, words) {
  // 一个单词的长度
  var oneWordLen = words[0].length;
  // 所有单词总长度
  var strLen = words.length * oneWordLen;
  var wordsMap = {}
  var res = []
  // 存单词以及出现的次数
  words.forEach(w => wordsMap[w] = (wordsMap[w] || 0) + 1)

  // i从0开始, 在 区间【i,strLen+i】 里 判断 截取单词出来 与 map 里的数据进行消除 
  // 外层跳出循环判断优化 i < s.length - strLen + 1
  for (let i = 0; i < s.length - strLen +1; i++) {
    let map = { ...wordsMap }
    let count = words.length
    for (let j = i; j < strLen + j; j += oneWordLen) {
      let str = s.substring(j, oneWordLen + j)
      // 这里跳出此次的清除操作 在 某一个区间里的 str  不存在于map 里 跳出 
      if (map[str] === undefined || map[str] === 0)break
      count--
      map[str]--
    }
    // count 为 0 清除完毕 结果正确
    if (count === 0) res.push(i)
  }

  return res
};

复杂度

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

思路

使用滑动窗口 + 双 Map

代码

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function (s, words) {
    if (!s || !words || !words.length) return []

    const wordsMap = new Map()
    for (const word of words) wordsMap.set(word, (wordsMap.get(word) || 0) + 1)

    const firstWordLen = words[0].length
    const allWordsLen = firstWordLen * words.length
    if (s.length < allWordsLen) return []

    const res = []
    for (let i = 0; i < firstWordLen; i++) {
        let left = i, right = i
        let count = 0, tmpMap = new Map()

        while (right + firstWordLen <= s.length) {
            const word = s.substring(right, right + firstWordLen)
            right += firstWordLen

            if (wordsMap.has(word)) {
                tmpMap.set(word, (tmpMap.get(word) || 0) + 1)
                count++
                while (tmpMap.get(word) > wordsMap.get(word)) {
                    let tmpWord = s.substring(left, left + firstWordLen)
                    count--
                    tmpMap.set(tmpWord, (tmpMap.get(tmpWord) || 0) - 1)
                    left += firstWordLen
                }
                (count == words.length) && res.push(left)
            } else {
                left = right
                tmpMap.clear()
                count = 0
            }
        }
    }
    return res
}

复杂度

时间复杂度: O(n), n = s.length

空间复杂度: O(m), m = words.length

potatoMa commented 2 years ago

思路


双滑动窗口+哈希表

代码


JavaScript Code

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    const len = words[0].length;
    const totalLen = len * words.length;
    if (s.length < totalLen) return [];
    // 根据words生成单词出现频率的哈希表
    const map = new Map();
    for (const word of words) {
        if (map.has(word)) {
            map.set(word, map.get(word) + 1);
        } else {
            map.set(word, 1);
        }
    }
    const res = [];
    let left = 0;
    while (left < s.length - totalLen + 1) {
        const window = s.slice(left, left + totalLen), tempMap = new Map(map);
        let right = 0;
        while (right < totalLen) {
            let temp = window.slice(right, right + len);
            if (tempMap.get(temp)) {
                tempMap.set(temp, tempMap.get(temp) - 1);
            }
            if (!Array.from(tempMap.values()).some(i => i !== 0)) {
                res.push(left);
                break;
            }
            right += len;
        }
        left++;
    }
    return res;
};

复杂度分析


时间复杂度:O(n * l),l为words长度

空间复杂度:O(n)

ff1234-debug commented 2 years ago

思路

哈希表 + 双指针

代码

实现语言: C++

class Solution {
public:
    vector<int> res;
    vector<int> findSubstring(string s_, vector<string>& words) {    
        // make hash
        unordered_map<string,int> hash;
        int w = words[0].size();
        for(string& word: words){
            if(hash.find(word)==hash.end())
                hash[word] = hash.size();
        }

        int dict[hash.size()]; memset(dict,0,hash.size()*sizeof(int));
        int note[hash.size()]; 
        for(string& word: words)
            dict[hash[word]]++;

        // make hash version s
        vector<int> s(s_.size(),-1);
        string buffer(w, ' ');
        for(int i=0;i<s.size()-w+1;i++){
            memcpy(&buffer[0],&s_[i],w);
            if(hash.find(buffer)!=hash.end())
                s[i] = hash[buffer];
        }

        // delete unuseful slice
        for(int i=0;i<w;i++){
            for(int j=i;j<s.size()-w+1;j+=w){
                if(s[j]==-1) continue;
                int start = j;
                while(j<s.size()-w+1&&s[j]!=-1)
                    j+=w;
                if( (j-start) / w >= words.size()) 
                    continue;
                else
                    for(int k=start;k<j;k+=w)
                        s[k] = -1;
            }
        }

        // start double pointer 
        for(int i=0;i<w;i++){
            for(int j=i;j<s.size()-w+1;j+=w){
                if(s[j]==-1) continue;
                // find a start position and clear the buffer
                memset(note,0,hash.size()*sizeof(int));
                int st = j, ed = j;
                while(ed<s.size()-w+1 && s[ed]!=-1) {
                    int indexEd = s[ed];
                    if( ++note[indexEd] > dict[ indexEd ]){
                        while(note[indexEd] > dict[indexEd]){
                            int indexSt = s[st];
                            note[indexSt]--;
                            st+=w;
                        }
                    }
                    if( (ed - st)/w +1 == words.size())
                        res.push_back(st);
                    ed += w;
                }
                j = ed;
            }
        }
        return res;
    }
};

复杂度分析

lilixikun commented 2 years ago

思路

哈希表 + 双指针

代码​

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    if (!words || !words.length) return[];
    let wordLen = words[0].length;
    let allWordsLen = wordLen * words.length;
    let ans = [], wordMap = {};
    for (let w of words) {
        wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
    }
    for (let i = 0; i < s.length - allWordsLen + 1; i++) {
        let wm = Object.assign({}, wordMap);
        for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
            let w = s.slice(j, j + wordLen);
            if (wm[w]) {
                wm[w]--
            } else {
                break;
            }
        }
        if (Object.values(wm).every(n => n === 0)) ans.push(i);
    }
    return ans;
};
Auto-SK commented 2 years ago

思路

  1. 总共用到两个哈希表,allWords 用于记录 words 中单词出现的次数,subWords 用于记录子串中(也就是滑动窗口中)单词出现的次数;
  2. wordNum 为单词的个数,wordLen 为单词长度;
  3. 遍历字符串,移动长度为 wordNum * wordLen 的滑动窗口,再在当前滑动窗口中依次比较 wordLen 长度的单词;
  4. 当这个窗口内一旦出现不存在 allWords 中的单词,或者这个单词在子串中出现的次数已经等于 allWords 中的次数(也就是再加入这个子串次数就要超出了),这个滑动窗口就不符合要求,直接 break 进入下一个滑动窗口的匹配;
  5. 一旦完全匹配上了,把滑动窗口的起始索引加入结果 res 中。

程序

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        allWords = collections.Counter(words)
        wordNum = len(words)
        wordLen = len(words[0])
        res = []
        for i in range(len(s) - wordNum * wordLen + 1):
            subWords = collections.defaultdict(int)
            index = i
            while index < i + wordNum * wordLen:
                curWord = s[index: index + wordLen]
                if curWord not in allWords or subWords[curWord] == allWords[curWord]:
                    break
                subWords[curWord] += 1
                index += wordLen
            if index == i + wordNum * wordLen:
                res.append(i)
        return res

复杂度

Tomtao626 commented 2 years ago

思路

joriscai commented 2 years ago

思路

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
  const ret = []
  // 统计出现的次数
  const map = {}
  for (let i = 0; i < words.length; i++) {
    const word = words[i]
    map[word] = map[word] || 0
    map[word]++
  }

  const sLen = s.length
  const wordLen = words[0].length
  const wordsNum = words.length

  const wordStrLen = wordLen * wordsNum
  for (let start = 0; start < sLen - wordStrLen + 1; start++) {
    // 当前窗口,当前用于判断的子串
    const subStr = s.slice(start, start + wordStrLen)
    // 记录个数
    const subMap = {}
    let subStart = 0
    // 处理是否符合,循环中会因不符合时跳出处理,故循环后要判断是否符合
    for (; subStart < subStr.length; subStart += wordLen) {
      const word = subStr.slice(subStart, subStart + wordLen)
      if (!(word in map)) break
      subMap[word] = subMap[word] || 0
      subMap[word]++

      if (subMap[word] > map[word]) break
    }
    // 上面循环处理了不成立的情况,当subStart在子串的末尾
    // 说明当前子串已经遍历完,且当前子串符合
    if (subStart === subStr.length) {
      ret.push(start)
    }
  }

  return ret
};

复杂度分析

HydenLiu commented 2 years ago

js

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function (s, words) {
  const wordLen = words[0].length;
  const substrLen = wordLen * words.length;
  const initialWordsMap = words.reduce((map, i) => {
    map[i] = (map[i] || 0) + 1;
    return map;
  }, {})
  const res = [];
  for (let i = 0; i <= s.length - substrLen; i++) {
    const wordsMap = { ...initialWordsMap };
    for (let j = i; j < i + substrLen; j += wordLen) {
      const word = s.slice(j, j + wordLen);
      if (!(word in wordsMap) || wordsMap[word] == 0) break;
      wordsMap[word]--;
    }
    const isUseWord = Object.values(wordsMap).every(v => v == 0)
    if (isUseWord) res.push(i);
  }
  return res;
};
learning-go123 commented 2 years ago

思路

将 words 转换为记录单词出现次数的哈希表, words_map = [foo:1 bar:1]

最后将转换后的 s(s1,s2,s3...) 序列按顺序放入到 words 构成的哈希表中去进行对比,例如将 s1 与 words_map 进行对比,可以发现 s1 满足 words_map 单词出现的次数(s1 中包含 [bar foo]),于是获得 s1 对应的单词起始位置 0

代码

Go Code:


func findSubstring(s string, words []string) []int {
    if len(s) == 0 || len(words) == 0 {
        return []int{}
    }
    wordsCnt := make(map[string]int, 0)
    for _, word := range words {
        wordsCnt[word]++
    }
    wordsLength := len(words)
    length := len(words[0])
    result := make([]int, 0)
    for index := 0; index+wordsLength*length <= len(s); index++ {
        ch := s[index : index+length]

        if _, ok := wordsCnt[ch]; ok {
            sCnt := make(map[string]int, 0)
            var j int
            for j = 0; j < wordsLength; j ++ {
                temp := s[index+j*length : index+(j+1)*length]
                sCnt[temp]++
                if sCnt[temp] > wordsCnt[temp] {
                    break
                }
            }
            if j == wordsLength {
                result = append(result, index)
            }
        }
    }
    return result
}

复杂度分析

令 n 为数组长度。

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

长见识ing。。map也可以equals

其实就是一个定长滑动窗口,一次增加单词的长度

有思路但是。。我太垃圾了

muimi commented 2 years ago

 ### 代码

// INPUT:
// s = "wordgoodgoodgoodbestword";
// words =  { "word", "good", "best", "good" };
// -----------------------------------------------
// OUTPUT:
// 0: word,good,good,good,best,word,
// 1: ordg,oodg,oodg,oodb,estw,
// 2: rdgo,odgo,odgo,odbe,stwo,
// 3: dgoo,dgoo,dgoo,dbes,twor,
// [8]
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> resList = new ArrayList<>();
    int word_num = words.length, one_word = words[0].length();
    for (int i = 0; i < one_word; i++) {
      Map<String, Integer> checkmap = new HashMap<>();
      int start_index = i, pointer = i, count = 0;
      while (pointer <= s.length() - one_word) {
        String word = s.substring(pointer, pointer + one_word);
        pointer += one_word; // for next loop
        if (!map.containsKey(word)) {
          count = 0;
          start_index = pointer;
          checkmap.clear();
        } else {
          checkmap.put(word, checkmap.getOrDefault(word, 0) + 1);
          count++;
          // backtracking
          while (checkmap.get(word) > map.get(word)) {
            String start_word = s.substring(start_index, start_index + one_word);
            count--;
            checkmap.put(start_word, checkmap.get(start_word) - 1);
            start_index += one_word;
          }
          if (count == word_num) resList.add(start_index);
        }
      }
    }
    return resList;
  }
}
guangshisong 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)
        if n < one_word:return []
        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
                if w not in words:
                    left = right
                    cur_Counter.clear()
                    cur_cnt = 0
                else:
                    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
JianXinyu commented 2 years ago

思路

题眼:words中的单词长度相同 遍历s中所有长度为 (words[0].length * words.length) 的子串 Y,查看 Y 是否可以由 words 数组构造生成。

yibenxiao commented 2 years ago

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

思路

Copy

https://www.bilibili.com/video/BV1nM4y1V7Wg(参考视频

代码

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)
        if n < one_word: return []
        # 记录每个单词出现的次数
        words = Counter(words)
        res = []

        # 只需要遍历字符串的一个word长度
        for i in range(0, one_word): # O(K)
            cur_cnt = 0
            # 双指针
            left = i
            right = i
            cur_Counter = Counter()
            while right + one_word <= n: # O(N)
                # 目前检索的单词
                w = s[right:right + one_word]
                right += one_word

                # 如果目前检索的单词不在words里,挪动左指针
                if w not in words:
                    left = right
                    cur_Counter.clear()
                    cur_cnt = 0
                else:
                    cur_Counter[w] += 1
                    cur_cnt += 1
                    # 如果目前统计的单词有超出words的单词计数,挪动左指针
                    while cur_Counter[w] > words[w]: # O(M)
                        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

复杂度

令 N 为字符串 S 长度, M 为 words 数组元素个数, K 为单个 word 字串长度

时间复杂度:O(N∗M∗K)

空间复杂度:O(M)

Lydia61 commented 2 years ago

串联所有单词的子串

思路

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        allWords = collections.Counter(words)
        wordNum = len(words)
        wordLen = len(words[0])
        res = []
        for i in range(len(s) - wordNum * wordLen + 1):
            subWords = collections.defaultdict(int)
            index = i
            while index < i + wordNum * wordLen:
                curWord = s[index: index + wordLen]
                if curWord not in allWords or subWords[curWord] == allWords[curWord]:
                    break
                subWords[curWord] += 1
                index += wordLen
            if index == i + wordNum * wordLen:
                res.append(i)
        return res

复杂发分析

Liuxy94 commented 2 years ago

思路

Counter + DFS

代码

class Solution:
    def findSubstring(self, S: str, W: List[str]) -> List[int]:
        N = len(S)
        C = Counter(W)

        # DFS with Counter 
        def dfs(i,counter):
            # if all words are squentially found return True
            if sum(counter.values()) == 0:
                return True

            for w in counter:
                if counter[w] and S[i:i+len(w)] == w:
                    counter[w] -= 1
                    if dfs(i + len(w), counter):
                        return True
                    counter[w] += 1
            return False

        # find length all words
        len_all_words = sum( len(w) * c for w,c in C.items())

        # for each letter in string search if we can make merge words from counter into the next substring
        ans = []
        for i in range(N-len_all_words+1):
            if dfs(i, deepcopy(C)):
                ans.append(i)
        return ans

复杂度分析

N - 字符串长度 NW - 单词数目 NL - 最大子串 LW - 所有单词长度

Time: O( (N-LW) NW NL ) Space: O(NW) * NW

ZETAVI commented 2 years ago

思路

哈希表+滑动窗口

语言

java

代码

public List<Integer> findSubstring(String s, String[] words) {
        HashMap<String, Integer> strMap = new HashMap<>();
        LinkedList<Integer> ans = new LinkedList<>();
        int step=words[0].length();
        for (String word:words){
            strMap.put(word,strMap.getOrDefault(word,0)+1);
        }
        for (int i = 0; i <= s.length()-step; i++) {
            int start=i;
            String sub=s.substring(start,start+step);
            if (!strMap.containsKey(sub))continue;
            else {
                HashMap<String,Integer> tempMap = (HashMap<String, Integer>) strMap.clone();
                do {
                    int left = tempMap.get(sub);
                    tempMap.put(sub,--left);
                    if (left==0)tempMap.remove(sub);
                    start+=step;
                    if (start+step>s.length())break;
                    sub=s.substring(start,start+step);
                }while (tempMap.containsKey(sub));
                if (tempMap.isEmpty())ans.offer(i);
            }
        }
        return ans;
    }

复杂度分析

pophy commented 2 years ago

思路

Java Code

    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<Integer>();
        int n = words.length;
        if (n == 0) {
            return res;
        }
        int d = words[0].length();
        HashMap<String, Integer> countMap = new HashMap<>();
        for (String w : words) {
            countMap.put(w, countMap.getOrDefault(w, 0) + 1);
        }
        for (int j = 0; j < d; j++) {
            HashMap<String, Integer> hasWords = new HashMap<>();
            int num = 0;
            for (int i = j; i < s.length() - n * d + 1; i = i + d) {
                boolean hasRemoved = false;
                while (num < n) {
                    String word = s.substring(i + num * d, i + (num + 1) * d);
                    if (countMap.containsKey(word)) {
                        int value = hasWords.getOrDefault(word, 0);
                        hasWords.put(word, value + 1);
                        if (hasWords.get(word) > countMap.get(word)) {
                            hasRemoved = true;
                            int removeNum = 0;
                            while (hasWords.get(word) > countMap.get(word)) {
                                String firstWord = s.substring(i + removeNum * d, i + (removeNum + 1) * d);
                                hasWords.put(firstWord, hasWords.get(firstWord) - 1);
                                removeNum++;
                            }
                            num = num - removeNum + 1;
                            i = i + (removeNum - 1) * d;
                            break;
                        }
                    } else {
                        hasWords.clear();
                        i = i + num * d;
                        num = 0;
                        break;
                    }
                    num++;
                }
                if (num == n) {
                    res.add(i);
                }
                if (num > 0 && !hasRemoved) {
                    String firstWord = s.substring(i, i + d);
                    int v = hasWords.get(firstWord);
                    hasWords.put(firstWord, v - 1);
                    num = num - 1;
                }
            }
        }
        return res;
    }

时间&空间

ru8dgj0001 commented 2 years ago

'''class Solution: def findSubstring(self, s: str, words: List[str]) -> List[int]: counter = Counter(words) M, W, K = len(s), len(words[0]), len(words) @lru_cache(None) def getWords(idx): if idx >= M: return [] w = s[idx: idx+W] return [w] + getWords(idx+W) wsm, ret = {}, [] for i in range(M): wsm[i] = getWords(i) for i in range(M): ws = wsm[i] c = Counter(ws[:K]) if c == counter: ret.append(i) return ret '''