30. 串联所有单词的子串

题目

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

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

示例 1

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

示例 2

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

示例 3:

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

提示:

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

 

暴力法

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

        vector<int> ret;
    	if(s.length()==0 || words.size()==0)
    		return ret;        
        unordered_map<string,int>m;
        for(auto word:words)//映射的初始化
        {
        	if(m.find(word) == m.end())
        		m[word]=1;
        	else
        		m[word]++;
        }
        int n = s.length();
        int size = words.size();
        int len = words[0].size();
        for(int i = 0; i<=n-size*len; i++)//遍历所有可能的起点进行判断 
        {            
            unordered_map<string,int> temp;
            int j = i;
            for(; j<i+size*len; j+=len) 
            {
                string word = s.substr(j, len);
                if(m.find(word) != m.end())
                {
                	if(temp.find(word) == temp.end())
                		temp[word]=1;
                	else
                		temp[word]++;
                    if(temp[word] > m[word])//如果此单词出现次数超出,则i位置不合法
                    	break;
                }
                else break;
            }

            if(j==i+size*len)
                ret.push_back(i);
        }

        return ret;
    }

};

 

滑动窗口法

  • 我们可以将起点根据当前下标与单词长度的取余结果进行分类,然后进行下面的滑动窗口(每次移动一个单词)操作,这样,我们枚举了所有起点分类的可能情况,这样可以做到不重不漏
  • 在起点的所属某一个类别的情况下,由于words中所有单词的长度是相同的,所以,我们滑动一个单词,下一个起点位置还是属于这个类别,并且我们可以复用上一个阶段的窗口内的单词的计数统计信息,不用再从头开始计数,我们通过不断的滑动,可以遍历所有以属于这个类别的起点的情况,外循环又枚举了所有的起点可以属于的类,这样就可以做到不重不漏

 

class Solution {

    public List<Integer> findSubstring(String s, String[] words) {

        List<Integer> ret = new ArrayList<>();
        int n = s.length();
        int m = words.length;
        int w_len = words[0].length();

        if(n == 0 || w_len == 0) 
            return ret;

        Map<String, Integer> wordsCount = new HashMap<>();
        for(String word : words)
        { 
            // 将所有单词加入 HashMap,并计数
            wordsCount.put(word, wordsCount.getOrDefault(word, 0) + 1);
        }

        for(int i = 0; i < w_len; ++i)
        {   
            // 错位循环,保证每种情况都遍历到
            Map<String, Integer> window = new HashMap<>();
            int left = i;
            int right = i;
            while(right+w_len <= n && left+w_len*m <= n)
            {
                String subRight = s.substring(right, right + w_len);
                // 如果这个单词不在 words 中,就重置窗口
                if(!wordsCount.containsKey(subRight))
                {
                    window.clear();
                    right += w_len;
                    left = right;
                    continue;
                }
                else
                {
                    // 将刚进入窗口并在 words 中的单词加入窗口 Hash 表
                    if(window.containsKey(subRight)) 
                        window.put(subRight, window.get(subRight)+1);
                    else 
                        window.put(subRight, 1);
                    // 当该单词在窗口中的出现次数多于在 words 中的出现次数时,
                    //不断删除窗口中最左边单词,直到次数相等
                    while(window.get(subRight) > wordsCount.get(subRight))
                    {
                        String subLeft = s.substring(left, left + w_len);
                        if(window.get(subLeft) == 1) 
                            window.remove(subLeft);
                        else 
                            window.put(subLeft, window.get(subLeft)-1);
                        left += w_len;
                    }
                    
                    // 当窗口长度正好等于 words 总长度时,表示匹配成功,加入结果中
                    if(right+w_len-left == w_len*m) 
                        ret.add(left);
                }

                right += w_len;
            }
        }
        return ret;
    }


};

  

posted on 2022-07-04 21:50  朴素贝叶斯  阅读(33)  评论(0编辑  收藏  举报

导航