当前位置: 首页 > article >正文

37-串联所有单词的子串

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

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

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

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

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:

  • 1 <= s.length <= 104
  • 1 <= words.length <= 5000
  • 1 <= words[i].length <= 30
  • words[i] 和 s 由小写英文字母组成

方法一:暴力枚举法

该方法通过遍历字符串 s 中所有可能的子串,将其按照 words 中单词的长度进行分割,然后检查分割后的单词是否与 words 中的单词完全匹配(不考虑顺序)。

function findSubstring(s: string, words: string[]): number[] {const result: number[] = [];if (s.length === 0 || words.length === 0) {return result;}const wordLength = words[0].length;const totalLength = wordLength * words.length;const wordCount = new Map<string, number>();for (const word of words) {wordCount.set(word, (wordCount.get(word) || 0) + 1);}for (let i = 0; i <= s.length - totalLength; i++) {const currentCount = new Map<string, number>();let j = 0;while (j < totalLength) {const word = s.slice(i + j, i + j + wordLength);if (!wordCount.has(word)) {break;}currentCount.set(word, (currentCount.get(word) || 0) + 1);if (currentCount.get(word)! > wordCount.get(word)!) {break;}j += wordLength;}if (j === totalLength) {result.push(i);}}return result;
}
复杂度分析
  • 时间复杂度:(O(n * m * k)),其中 n 是字符串 s 的长度,m 是 words 数组的长度,k 是 words 中每个单词的长度。因为需要遍历 s 中所有可能的子串,对于每个子串,需要检查其中的单词是否与 words 匹配。
  • 空间复杂度:(O(m)),主要用于存储 wordCount 和 currentCount 两个哈希表,其中 m 是 words 数组的长度。

方法二:滑动窗口法

使用滑动窗口来优化暴力枚举的过程,通过移动窗口来检查不同位置的子串是否满足条件。

function findSubstring(s: string, words: string[]): number[] {const result: number[] = [];if (s.length === 0 || words.length === 0) {return result;}const wordLength = words[0].length;const totalLength = wordLength * words.length;const wordCount = new Map<string, number>();for (const word of words) {wordCount.set(word, (wordCount.get(word) || 0) + 1);}for (let start = 0; start < wordLength; start++) {let left = start;let right = start;const currentCount = new Map<string, number>();let matchCount = 0;while (right + wordLength <= s.length) {const word = s.slice(right, right + wordLength);right += wordLength;if (wordCount.has(word)) {currentCount.set(word, (currentCount.get(word) || 0) + 1);if (currentCount.get(word)! <= wordCount.get(word)!) {matchCount++;}while (currentCount.get(word)! > wordCount.get(word)!) {const leftWord = s.slice(left, left + wordLength);currentCount.set(leftWord, currentCount.get(leftWord)! - 1);if (currentCount.get(leftWord)! < wordCount.get(leftWord)!) {matchCount--;}left += wordLength;}if (matchCount === words.length) {result.push(left);const leftWord = s.slice(left, left + wordLength);currentCount.set(leftWord, currentCount.get(leftWord)! - 1);matchCount--;left += wordLength;}} else {currentCount.clear();matchCount = 0;left = right;}}}return result;
}
复杂度分析
  • 时间复杂度:(O(n * k)),其中 n 是字符串 s 的长度,k 是 words 中每个单词的长度。因为对于每个起始偏移量,只需要遍历一次 s
  • 空间复杂度:(O(m)),主要用于存储 wordCount 和 currentCount 两个哈希表,其中 m 是 words 数组的长度。

方法三:递归回溯法

通过递归生成 words 所有可能的排列,然后在字符串 s 中查找这些排列对应的子串。

function findSubstring(s: string, words: string[]): number[] {const result: number[] = [];if (s.length === 0 || words.length === 0) {return result;}const wordLength = words[0].length;const totalLength = wordLength * words.length;const permutations = getPermutations(words);for (const permutation of permutations) {const target = permutation.join('');let index = s.indexOf(target);while (index!== -1) {result.push(index);index = s.indexOf(target, index + 1);}}return result;
}function getPermutations(arr: string[]): string[][] {const result: string[][] = [];const backtrack = (path: string[], used: boolean[]) => {if (path.length === arr.length) {result.push([...path]);return;}for (let i = 0; i < arr.length; i++) {if (used[i]) continue;path.push(arr[i]);used[i] = true;backtrack(path, used);path.pop();used[i] = false;}};backtrack([], new Array(arr.length).fill(false));return result;
}
复杂度分析
  • 时间复杂度:(O(m! * n)),其中 m 是 words 数组的长度,n 是字符串 s 的长度。因为需要生成 words 的所有排列,排列的数量为 (m!),对于每个排列,需要在 s 中查找其出现的位置。
  • 空间复杂度:(O(m! * m)),主要用于存储 words 的所有排列,排列的数量为(m!),每个排列包含 m 个单词。

你可以使用以下方式测试这些函数:

const s1 = "barfoothefoobarman";
const words1 = ["foo", "bar"];
console.log(findSubstring(s1, words1));const s2 = "wordgoodgoodgoodbestword";
const words2 = ["word", "good", "best", "word"];
console.log(findSubstring(s2, words2));const s3 = "barfoofoobarthefoobarman";
const words3 = ["bar", "foo", "the"];
console.log(findSubstring(s3, words3));

综上所述,滑动窗口法是解决该问题的最优方法,时间复杂度相对较低。

相关文章:

37-串联所有单词的子串

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcdef…...

ShardingSphere复合分片之hash槽算法

前言 上一篇《ShardingSphere复合分片》中有详细介绍多key多value的复合分片算法应该如何设计&#xff0c;在大部分情况下该算法是没有问题的&#xff0c;但是一旦涉及到数据迁移时&#xff0c;该算法的缺点就暴露无疑了。 为满足日益增长的用户或者订单的需求&#xff0c;在分…...

Web三漏洞学习(其二:sql注入)

靶场&#xff1a;NSSCTF 、云曦历年考核题 二、sql注入 NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过&#xff0c;但为了学习sql&#xff0c;整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判…...

KrillinAI:视频跨语言传播的一站式AI解决方案

引言 在全球内容创作领域&#xff0c;跨语言传播一直是内容创作者面临的巨大挑战。传统的视频本地化流程繁琐&#xff0c;涉及多个环节和工具&#xff0c;不仅耗时耗力&#xff0c;还常常面临质量不稳定的问题。随着大语言模型(LLM)技术的迅猛发展&#xff0c;一款名为Krillin…...

gravity`(控制 View 内部内容的对齐方式)

文章目录 **1. 常用取值****示例** **2. layout_gravity&#xff08;控制 View 在父容器中的对齐方式&#xff09;****常用取值****示例** **3. gravity vs layout_gravity 对比****4. 注意事项****5. 总结** 作用对象&#xff1a;当前 View 的内部内容&#xff08;如 TextView…...

gitdiagram源码架构分析

https://github.com/ahmedkhaleel2004/gitdiagram 整体架构分析 前端请求入口&#xff1a; 后端对应接口&#xff1a; 后端调试 后端调试&#xff1a;会提示api_key失败的问题&#xff1a; 有两种方法解决&#xff1a; 1、注释掉下面的行代码&#xff1b; 方法二&#xff1…...

蓝光三维扫描:汽车冲压模具与钣金件全尺寸检测的精准解决方案

随着汽车市场竞争日趋激烈&#xff0c;新车型开发周期缩短&#xff0c;安全性能要求提高&#xff0c;车身结构愈加复杂。白车身由多达上百个具有复杂空间型面的钣金件&#xff0c;通过一系列工装装配、焊接而成。 钣金件尺寸精度是白车身装配精度的基础。采用新拓三维XTOM蓝光…...

《分布式软总线:网络抖动下的数据传输“定海神针”》

在当下&#xff0c;智能设备之间的互联互通已成为生活与工作的刚需。分布式软总线作为实现这一愿景的关键技术&#xff0c;正日益凸显其重要性。然而&#xff0c;网络环境的复杂性&#xff0c;尤其是网络抖动频繁的情况&#xff0c;给分布式软总线的数据传输带来了严峻挑战。如…...

WPS JS宏编程教程(从基础到进阶)-- 第八部分:字符串技术与WPS结合应用

目录 第8章 字符串技术与WPS结合应用8-1 字符串的3种引用方式场景:动态生成报表标题三种引用方式对比代码解析表模板字符串核心优势8-2 字符串处理之切片与搜索场景:提取身份证中的出生年份三大截取方法对比方法选择指南索引搜索实战8-3 字符串处理之修改与填充场景:规范商品…...

C++实用函数:bind

本篇来介绍了C++中bind功能。 1 std::bind 在 C++ 里,std::bind 是一个函数模板,其作用是创建一个可调用对象,该对象可绑定到一组参数上。std::bind 的函数原型如下: template< class F, class... Args > /*unspecified*/ bind( F&& f, Args&&...…...

深度学习占用大量内存空间解决办法

应该是缓存的问题&#xff0c;关机重启内存多了10G&#xff0c;暂时没找到别的方法 重启前 关机重启后...

完全无网络环境的 openEuler 系统离线安装 ClamAV 的详细步骤

准备工作&#xff08;在外网机器操作&#xff09; 1. 下载 ClamAV RPM 包及依赖 mkdir -p ~/clamav-offline/packages cd ~/clamav-offline/packages# 使用 yumdownloader 下载所有依赖包&#xff08;需提前安装 yum-utils&#xff09; sudo dnf install yum-utils -y sudo y…...

Matlab绘制函数方程图形

Matlab绘制函数方程图形&#xff1a; 多项式计算: polyval 函数 Values of Polynomials: polyval ( ) 绘制方程式图形&#xff1a; 代码如下&#xff1a; >> a[9,-5,3,7]; x-2:0.01:5; fpolyval(a,x); plot(x,f,LineWidth,2); xlabel(x); ylabel(f(x))…...

Unity UI 从零到精通 (第30天): Canvas、布局与C#交互实战

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

在AMGX中使用MPI加载自定义分布式矩阵和向量

在AMGX中使用MPI加载自定义分布式矩阵和向量 AMGX是一个用于大规模并行代数多重网格求解的GPU加速库&#xff0c;支持MPI多线程环境。以下是加载用户自定义分布式矩阵和向量的方法&#xff1a; 1. 矩阵和向量分布的基本概念 在MPI环境中&#xff0c;AMGX使用行分布方式&…...

电视盒子 刷armbian

参考 中兴电视盒子中兴B860AV3.2-M刷Armbian新手级教程-CSDN博客 1.刷安卓9 带root版本 a. 下载安卓线刷包 链接&#xff1a;https://pan.baidu.com/s/1hz87_ld2lJea0gYjeoHQ8A?pwdd7as 提取码&#xff1a;d7as b.拆机短接 3.安装usbburning工具 使用方法 &#xff0c;…...

AI应用开发之扣子第一课-夸夸机器人

首先&#xff0c;进入官网&#xff1a;点击跳转至扣子。 1.创建智能体 登录进网站后&#xff0c;点击左上角&#xff0b;图标&#xff0c;创建智能体&#xff0c;输入智能体名称、功能介绍 2.输入智能体提示词 在“人设与回复逻辑”输入以下内容&#xff1a; # 角色 你是一…...

【计算机网络实践】(十二)大学校园网综合项目设计

本系列包含&#xff1a; &#xff08;一&#xff09;以太网帧分析与网际互联协议报文结构分析 &#xff08;二&#xff09;地址解析协议分析与传输控制协议特性分析 &#xff08;三&#xff09;交换机的基本操作、配置、 虚拟局域网配置和应用 &#xff08;四&#xff09;交…...

uniapp小程序位置授权弹框与隐私协议耦合(合而为一)(只在真机上有用,模拟器会分开弹 )

注意&#xff1a; 只在真机上有用&#xff0c;模拟器会分开弹 效果图&#xff1a; 模拟器效果图&#xff08;授权框跟隐私政策会分开弹&#xff0c;先弹隐私政策&#xff0c;同意再弹授权弹框&#xff09;&#xff1a; manifest-template.json配置&#xff08; "__usePr…...

【星闪模组开发板WS8204SLEBLEModule】星闪数据收发测试

目录 开发板简介 串口设置 主从模式设置 AT命令数据发送 透传模式数据发送 结语 本文首发于《电子产品世界》论坛&#xff1a;【星闪模组开发板WS8204SLE&BLEModule】星闪数据收发测试-电子产品世界论坛https://forum.eepw.com.cn/thread/392011/1 感谢eepw论坛和成…...

天梯赛L1-22-25

L1-022 奇偶分家 题目描述 给定 N 个正整数&#xff0c;统计其中奇数和偶数各有多少个。 输入格式 第一行&#xff1a;一个正整数 N&#xff08;≤1000&#xff09;。第二行&#xff1a;N 个非负整数&#xff0c;以空格分隔。 输出格式 在一行中输出奇数的个数和偶数的个…...

基础知识:Dify 错误排查

Case1:Dify 卡在管理员界面 查看容器状态 docker compose ps 可以看到有个容器异常:docker_db_1 的状态是 Restarting(表示一直在重启) 解决方案 参考:https://github.com/langgenius/dify/issues/5731...

spring cloud微服务断路器详解及主流断路器框架对比

微服务断路器详解 1. 核心概念 定义&#xff1a;断路器模式通过快速失败机制防止故障扩散&#xff0c;当服务调用出现异常或超时时&#xff0c;自动切换到降级逻辑&#xff0c;避免级联故障。核心功能&#xff1a; 熔断&#xff1a;在故障阈值&#xff08;如错误率&#xff09…...

BufferedReader 终极解析与记忆指南

BufferedReader 终极解析与记忆指南 一、核心本质 BufferedReader 是 Java 提供的缓冲字符输入流&#xff0c;继承自 Reader&#xff0c;通过内存缓冲和行读取功能极大提升文本读取效率。 核心特性速查表 特性说明继承链Reader → BufferedReader缓冲机制默认 8KB 字符缓冲…...

linux-设置每次ssh登录服务器的时候提醒多久需要修改密码

在 Linux 系统中,你可以通过设置 motd(Message of the Day)或 sshd 配置来在用户通过 SSH 登录时提醒他们密码即将过期。以下是具体步骤: 方法 1: 使用 motd 文件 motd 文件在用户登录时显示,你可以通过脚本动态生成内容,提醒用户密码过期时间。 编辑 /etc/motd 文件:…...

(小白0基础) 微调deepseek-8b模型参数详解以及全流程——训练篇

​ 本篇参考bilibili如何在本地微调DeepSeek-R1-8b模型_哔哩哔哩_bilibili 上篇&#xff1a;(小白0基础) 租用AutoDL服务器进行deepseek-8b模型微调全流程(Xshell,XFTP) —— 准备篇 初始变量 max_seq_length 2048 dtype None load_in_4bit True单批次最大处理模型大小dy…...

调用LLM的api

目录 chatgptdemo可选模型 chatgpt demo import openai openai.api_key xxxxxxxxx # 自己的api key openai.api_base https://api.feidaapi.com/v1 # 中转非直连 # response openai.ChatCompletion.create( # model"gpt-4o", # messages[ # {"rol…...

关于汽车辅助驾驶不同等级、技术对比、传感器差异及未来发展方向的详细分析

以下是关于汽车辅助驾驶不同等级、技术对比、传感器差异及未来发展方向的详细分析&#xff1a; 一、汽车辅助驾驶等级详解 根据SAE&#xff08;国际自动机工程师学会&#xff09;的标准&#xff0c;自动驾驶分为 L0到L5 六个等级&#xff1a; 1. L0&#xff08;无自动化&…...

windows安卓子系统wsa隐藏应用列表的安装激活使用

Windows 11 安卓子系统应用部署全攻略 windows安卓子系统wsa隐藏应用列表的安装激活使用|过检测核心前端 在 Windows 11 系统中&#xff0c;安卓子系统为用户带来了在电脑上运行安卓应用的便利。经过一系列的操作&#xff0c;我们已经完成了 Windows 11 安卓子系统的底层和前端…...

mongodb7日志特点介绍:日志分类、级别、关键字段(下)

#作者&#xff1a;任少近 上篇《mongodb7日志特点介绍&#xff1a;日志分类、级别、关键字段(上)》 链接: link 文章目录 4.日志会输出F/E/W/I四种情况5.日志关键字段6.日志量验证情况7.总结 4.日志会输出F/E/W/I四种情况 在MongoDB7中&#xff0c;日志输出按照严重性分为四种…...