每日一题——串联所有单词的子串

菜鸡每日一题系列打卡30

每天一道算法题目 

小伙伴们一起留言打卡

坚持就是胜利,我们一起努力!

题目描述(引自LeetCode)

给定一个字符串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"]
输出:[]

题目分析

这是一道字符串的子串的相关问题,对于这种问题,一种常见的处理方式是滑动窗口。通过两个位置指针所构成的窗口的移动,去判断子串是否符合条件,从而得到答案。详细的逻辑思路讲解见代码注释。

代码实现

class Solution {


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


        List<Integer> result = new ArrayList<>();


        // 特殊情况判断
        if (s == null || s.isEmpty() || words == null || words.length == 0) return result;


        Map<String, Integer> map = new HashMap<>();


        // 用哈希表存储words数组中的所有word与其出现次数之间的映射
        for (String word : words) map.put(word, map.getOrDefault(word, 0) + 1);


        // words数组的长度
        int allWords = words.length;


        // 每个word的长度
        int aWord = words[0].length();


        for (int i = 0; i < aWord; i++) {
            // start与end分别表示窗口的左右边界,flag表示匹配的单词个数
            int start = i, end = i, flag = 0;
            HashMap<String, Integer> tmp = new HashMap<>();
            // 滑动窗口
            while (end + aWord <= s.length()) {
                // 取单词
                String word = s.substring(end, end + aWord);
                // 右边界右移
                end += aWord;
                // 匹配成功,添加至tmp
                if (map.containsKey(word)) {
                    tmp.put(word, tmp.getOrDefault(word, 0) + 1);
                    flag++;
                    // 一个单词匹配多次,左边界右移
                    while (tmp.getOrDefault(word, 0) > map.getOrDefault(word, 0)) {
                        tmp.put(s.substring(start, start + aWord), tmp.getOrDefault(s.substring(start, start + aWord), 0) - 1);
                        flag--;
                        start += aWord;
                    }
                    if (flag == allWords) result.add(start);
                } 
                // 匹配失败,重置各项条件
                else {
                    tmp.clear();
                    flag = 0;
                    start = end;
                }
            }
        }


        // 返回结果
        return result;
        
    }


}

代码分析

对代码进行分析,由代码的具体循环逻辑中的访问次数可得,代码的时间复杂度为O(n),而就空间而言,需要用哈希表对word进行存储,因此,在最坏的情况下,代码的空间复杂度为O(n)。

执行结果

写在题目之后

转眼之间,每日一题已经陪伴大家一个月了。

在过去的一个月里,无论忙碌或者清闲,每天都有每日一题如影随形。

最近菜鸡又新开了每日一面栏目,每日系列大家庭更加壮大了。

在这里,菜鸡要特别感谢Bjamekharstar回忆遗忘CCC北辰远等所有小伙伴的陪伴。

是你们的期待,给了菜鸡不断更的信念。

最后,特别感谢我的老婆,你设计的菜鸡形象始终在提醒我——

不忘初心,止于至善。

学习 | 工作 | 分享

????长按关注“有理想的菜鸡

只有你想不到,没有你学不到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值