首页 > 其他分享 >30. 串联所有单词的子串

30. 串联所有单词的子串

时间:2024-10-11 18:17:39浏览次数:3  
标签:子串 30 len 单词 words 字符串 wordLength

目录

一、问题描述

二、解题思路

三、代码

四、复杂度分析


一、问题描述

给定一个字符串 s 和一个字符串数组 words words 中所有字符串 长度相同

 s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

  • 例如,如果 words = ["ab","cd","ef"], 那么 "abcdef", "abefcd""cdabef", "cdefab""efabcd", 和 "efcdab" 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

二、解题思路

这个问题要求找到字符串 s 中的所有“串联子串”,这些子串由字符串数组 words 中的所有字符串组成,并且每个子串中的字符串都必须是 words 中的元素,且每个元素只能使用一次。具体的思路如下:

  1. 固定子串长度:每个子串的总长度是 words 中每个字符串的长度乘以 words 中的字符串数量。即,子串的长度为 len(words) * len(words[0])

  2. 滑动窗口:我们可以通过滑动窗口的方式遍历字符串 s,每次检查长度为 len(words) * len(words[0]) 的子串,判断该子串是否由 words 中的字符串拼接而成。

  3. 使用哈希表计数:为了判断一个子串是否包含 words 中的所有字符串,我们可以使用一个哈希表记录 words 中每个字符串的出现次数,并在检查每个子串时,对其进行匹配。

  4. 边界条件:如果 s 的长度小于 len(words) * len(words[0]),则直接返回空列表。

具体步骤:

  1. 创建哈希表 word_count:用于记录 words 中每个字符串的出现次数。
  2. 遍历 s:从 s 的每个起点开始,取出长度为 len(words) * len(words[0]) 的子串,并检查是否是由 words 中的字符串组成的。
  3. 滑动窗口检查:对每个子串使用另一个哈希表记录匹配到的字符串次数,逐个比较是否与 word_count 一致。
  4. 返回结果:将所有符合条件的起始索引返回。

优化思路:

  1. 滑动窗口:通过滑动窗口,每次移动固定长度的窗口,而不是每次都重新构建和检查整个单词频率表。
  2. 跳过无效起点:如果某个起点的第一个单词不在 words 中,可以直接跳过,而不是逐个检查。

三、代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> result = new ArrayList<>();
        if (s == null || s.length() == 0 || words == null || words.length == 0) {
            return result;
        }

        int wordLength = words[0].length();  // 单个单词的长度
        int totalWordsLength = wordLength * words.length;  // 所有单词拼接起来的总长度
        if (s.length() < totalWordsLength) {
            return result;
        }

        // 记录 words 中每个单词出现的次数
        Map<String, Integer> wordCount = new HashMap<>();
        for (String word : words) {
            wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
        }

        // 遍历可能的起始位置
        for (int i = 0; i < wordLength; i++) {
            // 利用滑动窗口遍历整个字符串
            int left = i, right = i;
            int count = 0;  // 记录匹配的单词数
            Map<String, Integer> seen = new HashMap<>();

            while (right + wordLength <= s.length()) {
                // 取出当前窗口右侧的单词
                String word = s.substring(right, right + wordLength);
                right += wordLength;

                if (wordCount.containsKey(word)) {
                    seen.put(word, seen.getOrDefault(word, 0) + 1);
                    count++;

                    // 如果某个单词的数量超出了 words 中的要求,则需要移动左侧窗口
                    while (seen.get(word) > wordCount.get(word)) {
                        String leftWord = s.substring(left, left + wordLength);
                        seen.put(leftWord, seen.get(leftWord) - 1);
                        count--;
                        left += wordLength;
                    }

                    // 当匹配到所有单词时,将当前左侧窗口的位置加入结果
                    if (count == words.length) {
                        result.add(left);
                    }
                } else {
                    // 如果当前单词不在 words 中,重置窗口
                    seen.clear();
                    count = 0;
                    left = right;
                }
            }
        }

        return result;
    }
}

四、复杂度分析

  • 外层循环遍历起始位置:O(wordLength)
  • 内层循环遍历所有字符,每次移动 wordLength 长度,整个遍历次数为 O(n),其中 n 是字符串 s 的长度。
  • 因此,整体时间复杂度为 O(wordLength * n),比之前直接比较每一个可能位置更高效。
  • 空间复杂度:O(m),因为我们需要额外的哈希表来记录 words 中的单词以及窗口中的单词。

标签:子串,30,len,单词,words,字符串,wordLength
From: https://blog.csdn.net/weixin_61695887/article/details/142834002

相关文章

  • 2024最新最全:网络安全人士【必备的30个安全工具】
    1.WiresharkWireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是截取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。2.MetasploitMetasploit是一个免费的、可下载的框架,通过它可以很容易......
  • 20222307 2024-2025-1 《网络与系统攻防技术》实验一实验报告
    1.实验内容1.1本周学习内容进程内存管理在Linux系统中,当OS可执行程序被加载到内存后,其内存布局主要包括三个关键段:*.text段:包含程序的指令,这些指令是只读的,用于指导CPU执行操作。*.data段:存储静态初始化数据,这些数据是可写的,程序在运行时可以直接访问和修改。*.bss段:用......
  • 20222302 2024-2025-1 《网络与系统攻防技术》实验一实验报告
    1.实验内容本周学习内容1.熟练掌握了栈和堆的概念。2.掌握了Linux的基本操作,如shell命令和编译器gcc、调试器gdb的使用。3.掌握了缓冲区溢出的原理。实验任务本次实验的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何......
  • ABC302G Sort from 1 to 4 [关键性质题]
    Description给定一个长度为\(N\)的序列,其中每个元素都是介于\(1\)和\(4\)之间的整数。可以进行以下操作任意次(可能为零次):选择一对整数\((i,j)\),其中\(1≤i<j≤N\),并交换\(A_i\)和\(A_j\)。输出使序列\(A\)变为非递减序列所需的最小操作次数。Solution对于这......
  • GD32F303移植FreeRTOS-Plus-CLI
    FreeRTOS移植好是没有命令行交互的,刚好系统提供了相关的代码,那么就方便多了。示例基于FreeRTOS-Kernel-9.0.0.zip,再次之前需要把系统移植完毕。移植FreeRTOS-Plus-CLI需要干好几件事串口初始化串口终端服务函数添加自己定制的命令下面讲添加哪些文件新建一个分组,放CLI相......
  • 实现远距离通信 PS304数字接口转发器实现UART转换为I2C、SPI、1Wire等多种数字接口!
    实现远距离通信PS304数字接口转发器实现UART转换为I2C、SPI、1Wire等多种数字接口!PS304多种数字接口物理层协议转发器,能够实现UART转换为I2C、SPI、1Wire等其他数字接口,以实现远距离通信。该转发器具备内嵌磁隔离双电源及辅助增强电源电路、自适应线缆算法和强大灵活的S2S协议......
  • 设计方案:FMC303-两路5.6Gsps 14bit DA FMC子卡
    一、板卡概述    FMC303可实现宽波段、双通道、14位、5.6GSPS(2.8gsps直接射频综合)DAC功能,时钟可采用内部时钟源(可选择锁定到外部参考),或外部提供的采样时钟。此外还为用户提供定制采样控制的触发器输入。FMC303在机械上和电气上符合FMC标准(ANSI/VITA 57.1)。该卡具有多引脚连......
  • 20222308 2024-2025-1 《网络与系统攻防技术》实验一实验报告
    1.实验内容本次式样具体内容是通过三种方法,运行pwn1可执行文件,调用getshell。缓冲区溢出作为一种非常致命的攻击,它会使攻击者直接破坏堆栈保护,非法获取数据。完成本次实验,需要具备以下知识和技能基础:创建kali虚拟机,连接网络等这个部分对我来说还是出现了比较大的波折,我的操......
  • 天涯神贴2024最新整理核心300+篇
    天涯神贴天涯社区创办于1999年,逐步成为了无数网民的精神家园。在2007年,天涯的注册用户突破了2000万,社区开始自称为“全球最大的中文互联网社区”,并喊出了“全球华人的网上家园”这一口号。天涯社区不仅是一个论坛,更是许多网民在互联网世界中的第一份归属感。在这个平台上,诞......
  • 300道金典Java面试题,常见面试题及答案汇总
    Q1:Java中变量可以既是局部变量又是静态变量吗?答案:不能,将局部变量定义为静态变量会导致编译错误。Q2:Interface中可以有静态方法吗?答案:Interface中的静态方法是没有意义的,静态方法在类中不能被覆盖,而Interface中的方法默认都是抽象的,所以只能在实现Interface的类中实现。Q3:在......