L30. 串联所有单词的子串

给定一个字符串 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.暴力方法:
因为单词长度固定的,我们可以计算出截取字符串的单词个数是否和 words 里相等,长度都是相同的,先截取一个完整长度,然后再对其计数判断,双重循环搞定。

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<>();//盛放word的map,
        //计数功能

        int wordLen = words[0].length();
        int allLen = words.length * wordLen;//总长度

        for(String word : words){
            //放入map进行计数
            map.put(word, map.getOrDefault(word, 0) + 1);
        }


        //暴力循环,并与map进行比对,一旦相同,则满足条件
        for(int i = 0; i <= s.length() - allLen; i++){
            //截取一个完整的Words
            String tmp = s.substring(i, i + allLen);//这里不用-1
            //构建新的map,与原map比对
            HashMap<String, Integer> tmpMap = new HashMap<>();

            //挨个截取Word长度,map负责标记
            for(int j = 0; j < allLen; j += wordLen){
                String word = tmp.substring(j, j + wordLen);
                //截取一个Word,从0开始,直接加上长度就是了

                tmpMap.put(word, tmpMap.getOrDefault(word, 0) + 1);
            }
            //判断两个map是否相等
            if(map.equals(tmpMap)){
                res.add(i);
            }
        }
        return res;
    }
}

2.对上述双重循环进行简化;
即滑动窗口法,每次在判断两个map是否相等时,有很多重复的判断

注意唯一不同的是,滑动窗口开始位置有多种可能,所以外层有for循环

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<>();//盛放word的map,
        //计数功能

        int wordLen = words[0].length();
        int allLen = words.length * wordLen;//总长度

        for (String word : words) {
            //放入map进行计数
            map.put(word, map.getOrDefault(word, 0) + 1);
        }


        for (int i = 0; i < wordLen; i++) {
            //起始位置有不同种情况,恰好wordlen种组合情况

            HashMap<String, Integer> tmpMap = new HashMap<>();
            //套用其模板
            int left = i, right = i, cnt = 0;//因为是在循环内部
            while (right + wordLen <= s.length()) {
                //由于right是作为subString的右边界,所以这里取等号
                String w = s.substring(right, right + wordLen);
                //要不要取等号,取决于这一句

                tmpMap.put(w, tmpMap.getOrDefault(w, 0) + 1);
                cnt++;//计算Word个数

                while (tmpMap.getOrDefault(w, 0) > map.getOrDefault(w, 0)) {
                    //left要开始移动了,将窗口缩到最小
                    String now = s.substring(left, left + wordLen);
                    //常用的方法,就是起始位置+长度就是右边界
                    tmpMap.put(now, tmpMap.getOrDefault(now, 0) - 1);
                    left += wordLen;
                    cnt--;
                }
                right += wordLen;
                if (cnt == words.length) res.add(left);//这里就是添加left
            }

        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值