30.串联所有单词的子串

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

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例
输入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
输出:[]

算法思路

刚开始拿到这个题,我的思路是找到单词words中所有能组合成的字符串,然后逐个在s中比较,得到结果。这种方法明显的不足在于将words中的所有单词进行排列组合,会耗费大量的和空间,当单词数量较多是,组合数也会特别多,很多组合都是不必要的,所有排除这种了思路。

另一种思路:滑动窗口,引入HashMap的键值对来解题。具体步骤如下:

  • 首先将words中的每个单词及其出现的次数作为键值对加入map中,将单词word作为键,对应单词出现的次数作为值,使用getOrDefault(key, default value)方法;
  • 从目标字符串s的第一个字符开始,不用循环到最后一个字符,到s的长度减去words组成的字符串长度即可;
  • 然后从i开始,长度为所有word组成的字符串的长度,每次讲j加单个word的长度,依次取出单个word及其出现的次数加入新的map中;
  • 最后判断两个map是否相等即可。

该方法可以在第二层循环中优化一下,减少没必要的遍历。首先是判断子串substr是否是map中的key,不存在就直接跳出当循环,遍历下一个i,存在就判断当前map中的key对应出现的次数是否比map中的大,大于就天出循环。

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if(s.isEmpty() || words.length==0 || s.length()<words[0].length()) return res;
        Map<String,Integer> map = new HashMap<>();
        int oneLen = words[0].length();
        int allLen = oneLen * words.length;
        for(String word:words ){
            map.put(word,map.getOrDefault(word, 0)+1);   //将words中每一个子单词作为键,出现的次数作为值
        }
        for(int i = 0;i<s.length()-allLen+1;i++){   
            Map<String,Integer> temp = new HashMap<>();
            for(int j = i;j < i+allLen;j+=oneLen){//从i开始逐个单词加入hashMap中,然后和map进行比较
                String substr = s.substring(j,j+oneLen);
                temp.put(substr,temp.getOrDefault(substr,0)+1);
                if(map.containsKey(substr)){//判断substr在map中是否存在,如果存在然后再判断对应的值是否比map中的大,如果大,就结束当前循环
                    if(temp.get(substr) > map.get(substr))  
                        break;
                }
                else break;//substr不在map的键中,结束当前循环
            }
            if(map.equals(temp)) res.add(i); //HashMap集合中上的键值对与顺序无关
        }
        return res;
    }
}

复杂度
时间复杂度:O(MN),M是字符串s的长度,N是words数组的长度;
空间复杂度: O(N),引入了HashMap,N个键值对。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值