15.LeetCode 30. 串联所有单词的子串(Java):滑动窗口+哈希表详解

目录

[1. 题目解析](#1. 题目解析)

[2. 讲解算法原理](#2. 讲解算法原理)

[3. 代码实现(完整保留)](#3. 代码实现(完整保留))


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

1. 题目解析

题目:串联所有单词的子串

给定一个字符串 s和一个单词数组 words,words中所有单词长度相同。要求找出 s中串联子串 的起始位置------即包含 words中所有单词(顺序不限)连续拼接而成的子串。

  • 示例1:

    输入:s = "barfoothefoobarman", words = ["foo","bar"]

    输出:[0,9]

    解释:words长度为2,words[0]长度为3,故子串长度需为 2×3=6。

    • 子串 "barfoo"从位置0开始,是 ["bar","foo"]的排列;

    • 子串 "foobar"从位置9开始,是 ["foo","bar"]的排列。

      返回顺序无关,如 [9,0]也可。

2. 讲解算法原理

解法:滑动窗口 + 哈希表

参考类似题目(如438. 找出所有的字母异位词),本题核心是用滑动窗口覆盖所有单词的拼接,并通过哈希表统计单词频次。

  • 关键思路:

    1. 哈希表 :用 hash1存 words中每个单词的出现次数;用 hash2存滑动窗口内单词的出现次数。

    2. 指针移动 :左指针 left、右指针 right按单词长度 (len)移动(因为单词长度固定)。

    3. 滑动窗口执行次数 :外层循环次数为 len(因为单词长度可能为1~len,需覆盖所有可能的起始偏移)。

示意图理解:

  • 示例:s = "barfoothefoobarman", words = ["foo","bar"]

  • 滑动窗口的移动,以及 hash1(words频次)、hash2(窗口内频次)的对比。

3. 代码实现(完整保留)

java 复制代码
class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> ret = new ArrayList<Integer>();
        
        Map<String, Integer> hash1 = new HashMap<String, Integer>(); // 保存字典中所有单词的频次
        for (String str : words) hash1.put(str, hash1.getOrDefault(str, 0) + 1);
        
        int len = words[0].length(), m = words.length;
        
        for (int i = 0; i < len; i++) { // 执行次数:单词长度的循环
            Map<String, Integer> hash2 = new HashMap<String, Integer>(); // 保存窗口内所有单词的频次
            for (int left = i, right = i, count = 0; right + len <= s.length(); right += len) {
                // 进窗口 + 维护 count
                String in = s.substring(right, right + len);
                hash2.put(in, hash2.getOrDefault(in, 0) + 1);
                if (hash2.get(in) <= hash1.getOrDefault(in, 0)) count++;
                
                // 出窗口条件:窗口长度超过 单词总长度(len*m)
                if (right - left + 1 > len * m) {
                    String out = s.substring(left, left + len);
                    if (hash2.get(out) <= hash1.getOrDefault(out, 0)) count--;
                    hash2.put(out, hash2.get(out) - 1);
                    left += len;
                }
                
                // 更新结果:count等于单词总数m时,记录left
                if (count == m) ret.add(left);
            }
        }
        return ret;
    }
}
相关推荐
2601_970703831 小时前
工业除湿机租赁:把一份租金合同拆开看
大数据·数据结构·算法·数据库架构·推荐算法
心中有你02141 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
ZhengEnCi2 小时前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
Flynn Liang3 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
liminla!3 小时前
SQL 从入门到进阶:语法速查与实战练习(完整优化版)
数据库·sql·算法
维克兜率天4 小时前
【维克】指数调整:哪些股票会被“强制买入“?
人工智能·python·算法
罗西的思考5 小时前
【轻量化复刻大模型】线性注意力交互式演示
人工智能·算法·机器学习
神也畏惧菠萝5 小时前
Day08 Python流程控制从入门到上瘾:if/match/while/for四件套,从B站登录一路打到猜数字游戏
python·算法·数据存储·for·while·if·ifelse
揽星河..5 小时前
【C语言】分支和循环
c语言·c++·算法