力扣第三十题——串联所有单词的子串

devtools/2024/9/20 0:24:40/ 标签: leetcode, 算法, 职场和发展

内容介绍

给定一个字符串 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 由小写英文字母组成

完整代码

 typedef struct {char key[32];int val;UT_hash_handle hh;
} HashItem;int* findSubstring(char * s, char ** words, int wordsSize, int* returnSize){    int m = wordsSize, n = strlen(words[0]), ls = strlen(s);int *res = (int *)malloc(sizeof(int) * ls);int pos = 0;for (int i = 0; i < n; i++) {if (i + m * n > ls) {break;}HashItem *diff = NULL;char word[32] = {0};for (int j = 0; j < m; j++) {snprintf(word, n + 1, "%s", s + i + j * n);HashItem * pEntry = NULL;HASH_FIND_STR(diff, word, pEntry);if (NULL == pEntry) {pEntry = (HashItem *)malloc(sizeof(HashItem));strcpy(pEntry->key, word);pEntry->val = 0;HASH_ADD_STR(diff, key, pEntry);} pEntry->val++;            }for (int j = 0; j < m; j++) {HashItem * pEntry = NULL;HASH_FIND_STR(diff, words[j], pEntry);if (NULL == pEntry) {pEntry = (HashItem *)malloc(sizeof(HashItem));strcpy(pEntry->key, words[j]);pEntry->val = 0;HASH_ADD_STR(diff, key, pEntry);} pEntry->val--;if (pEntry->val == 0) {HASH_DEL(diff, pEntry);free(pEntry);}}for (int start = i; start < ls - m * n + 1; start += n) {if (start != i) {char word[32];snprintf(word, n + 1, "%s", s + start + (m - 1) * n);HashItem * pEntry = NULL;HASH_FIND_STR(diff, word, pEntry);if (NULL == pEntry) {pEntry = (HashItem *)malloc(sizeof(HashItem));strcpy(pEntry->key, word);pEntry->val = 0;HASH_ADD_STR(diff, key, pEntry);} pEntry->val++;if (pEntry->val == 0) {HASH_DEL(diff, pEntry);free(pEntry);}snprintf(word, n + 1, "%s", s + start - n);pEntry = NULL;HASH_FIND_STR(diff, word, pEntry);if (NULL == pEntry) {pEntry = (HashItem *)malloc(sizeof(HashItem));strcpy(pEntry->key, word);pEntry->val = 0;HASH_ADD_STR(diff, key, pEntry);} pEntry->val--;if (pEntry->val == 0) {HASH_DEL(diff, pEntry);free(pEntry);}}if (HASH_COUNT(diff) == 0) {res[pos++] = start;}}HashItem *curr, *tmp;HASH_ITER(hh, diff, curr, tmp) {HASH_DEL(diff, curr);  free(curr);      }}*returnSize = pos;return res;
}

思路详解

整体思路

  1. 初始化:确定单词长度n,单词数量m,字符串s的长度ls,并分配一个数组res用于存储结果。

  2. 遍历所有可能的起始位置:由于每个单词的长度是n,所以可能的起始位置最多有n个。

  3. 构建哈希表:对于每个起始位置,构建一个哈希表来存储words数组中每个单词出现的次数。

  4. 检查子串:从当前起始位置开始,每次移动一个单词长度n,检查当前子串是否由words中的单词组成。

  5. 更新哈希表:每次移动子串时,更新哈希表中的计数,如果计数为0,则从哈希表中删除。

  6. 记录结果:如果哈希表为空,说明找到了一个符合条件的子串,记录其起始索引。

  7. 清理哈希表:在每次循环结束后,清理哈希表,释放内存。

详细步骤

  1. 初始化变量mwords数组的长度,n是每个单词的长度,ls是字符串s的长度。res数组用于存储结果,pos用于记录结果数组的当前索引。

  2. 外层循环:遍历所有可能的起始位置i,最多有n个。如果起始位置加上m * n超过s的长度,则结束循环。

  3. 构建哈希表:对于每个起始位置,创建一个哈希表diff,并初始化它,将words数组中的每个单词加入哈希表,并设置其值为1。

  4. 内层循环:从当前起始位置开始,每次向前移动一个单词长度n,进行以下操作:

    • 如果不是起始位置,则更新哈希表,移除当前子串的第一个单词,并添加新的单词。
    • 检查哈希表是否为空,如果为空,则说明找到了一个符合条件的子串,记录其起始索引。
  5. 清理哈希表:在每次外层循环结束后,遍历哈希表,删除所有条目,并释放内存。

  6. 返回结果:将结果数组的长度赋值给returnSize,并返回结果数组res

代码注释说明

  • HashItem:定义了一个哈希表项,包含一个字符串key和一个整数值val,以及一个哈希表句柄hh
  • findSubstring:主函数,用于找到所有字母异位词的起始索引。
  • snprintf:用于从字符串s中提取子串。
  • HASH_FIND_STR:在哈希表中查找一个字符串。
  • HASH_ADD_STR:向哈希表添加一个字符串。
  • HASH_DEL:从哈希表中删除一个项。
  • HASH_COUNT:计算哈希表中的项数。

知识点精炼

哈希表应用

  1. 哈希表结构:使用结构体HashItem存储单词及其出现次数,利用UT_hash_handle进行哈希表操作。
  2. 哈希表操作:掌握HASH_FIND_STRHASH_ADD_STRHASH_DEL等宏,用于查找、添加和删除哈希表项。

字符串处理

  1. 子串提取:使用snprintf函数从源字符串中提取指定长度的子串。
  2. 字符串比较:利用哈希表进行字符串比较,避免直接使用字符串比较函数。

滑动窗口

  1. 窗口大小:窗口大小为单词数组words的总长度,即m * n
  2. 窗口移动:每次移动一个单词长度n,更新哈希表,进行窗口内字符串的检查。

双指针技术

  1. 起始指针:外层循环控制窗口的起始位置。
  2. 移动指针:内层循环控制窗口的移动,更新哈希表。

内存管理

  1. 动态分配:使用malloc为结果数组分配内存。
  2. 内存释放:使用free释放哈希表项和结果数组的内存。

循环与条件判断

  1. 边界检查:确保子串起始位置不会超出字符串s的长度。
  2. 哈希表检查:通过HASH_COUNT判断哈希表是否为空,以确定是否找到符合条件的子串。

性能优化

  1. 减少重复操作:通过只在窗口移动时更新哈希表,减少不必要的操作。
  2. 及时清理:在每次外层循环结束后清理哈希表,避免内存泄漏。

 


http://www.ppmy.cn/devtools/85006.html

相关文章

谷粒商城实战笔记-54-商品服务-API-三级分类-拖拽效果

文章目录 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果1&#xff0c;el-tree控件加上允许拖拽的属性2&#xff0c;是否允许拖拽3&#xff0c;完整代码 一&#xff0c;54-商品服务-API-三级分类-修改-拖拽效果 本节的主要内容是给三级分类树形结构加上拖拽功能&#…

学习pcie—20240724

因为前一段时间看了xdma的IP核手册&#xff0c;发现只看xdma看不太懂&#xff0c;不清楚pcie的传输的流程&#xff0c;不了解报文格式&#xff0c;所以看看pcie手册&#xff0c;主要关注发送接收时序 首先是pcieIP核与xdmaIP核的区别&#xff1a; Integrated Block for PCI E…

Android 13 大屏显示时关于SystemUI和Launcher3问题

当系统运行在大屏上时,原来显示SystemUI导航栏的位置会变成Launcher3的任务栏,然后导航栏的3个按键显示靠右下角显示 我的博客 1.先看SystemUI的导航栏为什么会消失,移动 /SystemUI/src/com/android/systemui/statusbar/NavigationBarController.javapublic void createNavigat…

LeetCode Hot100 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确…

【iOS】—— 类与对象的底层

【iOS】—— 类与对象的底层 1. OC编译生成C代码的方法1.1 clang1.2 xcrun 2. 对象的本质2.1 objc_class 和 objc_object 有什么关系?2.2 objc_object与对象的关系&#xff1a; 3. 类结构3.1 Class isa&#xff08;1&#xff09;isa指针是什么&#xff1f; &#xff08;2&…

MySQL第四次作业

先创建库和表 处理表 1. 修改 student 表中年龄(sage)字段属性&#xff0c;数据类型由 int 改变为 smallint ALTER TABLE student MODIFY sage SMALLINT; 2. 为 Course 表中 Cno 课程号字段设置索引&#xff0c;并查看索引 ALTER TABLE course ADD INDEX index_cno (Cno); …

ARM32开发——PWM蜂鸣器案例

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 需求原来的驱动移植操作替换初始化 更新Play函数完整代码 需求 通过控制PB9来播放音乐&#xff0c;PB9对应的定时器通道&#xff1…

基于上云api前端开发经验教训(loading...)

问题一&#xff1a;部署前端代码时npm报错 由于npm源在国外&#xff0c;出现安装异常或比较慢的情况&#xff0c;使用cnpm(淘宝镜像)来解决。 安装cnpm npm install -g cnpm --registryhttp://registry.npmmirror.com使用cnpm(同npm一样) cnpm install

【MySQL进阶之路 | 高级篇】范式概述与第一范式

1. 范式简介 在关系型数据库中&#xff0c;关于数据表的设计的基本原则&#xff0c;规则就称为范式。可以理解为&#xff0c;一张数据表的设计结果需要满足的某种设计标准的级别。要想设计一个结构合理的关系型数据库&#xff0c;必须满足一定的范式。 范式的英文名是Normal …

苍穹外卖(一)之环境搭建篇

Ngnix启动一闪而退 启动之前需要确保ngnix.exe的目录中没有中文字体&#xff0c;在conf目录下的nginx.conf文件查看ngnix的端口号&#xff0c;一般默认为80&#xff0c;若80端口被占用就会出现闪退现象。我们可以通过logs/error.log查看错误信息&#xff0c;错误信息如下&…

前端路由快速上手-React-Router

1. 前端路由简介 前端路由是一种在单页面应用&#xff08;SPA&#xff09;中实现页面跳转的技术&#xff0c;它允许我们通过改变URL地址而无需重新加载页面来显示不同的内容。在前端路由中&#xff0c;每个路径&#xff08;path&#xff09;都对应一个组件&#xff08;compone…

C语言学习笔记

前言 ———————————————————— ——c语言是各大编程技术的基础&#xff0c;成为优秀的程序员&#xff0c;应当对c熟练掌握并且拥有自己的理解。我将在这里持续更新本人从零基础开始学习c语言中的学习笔记&#xff0c;个人理解及感受。一方面为了记录我在学习中…

11 - FFmpeg - 编码 AAC

Planar 模式是 ffmpeg内部存储模式&#xff0c;我们实际使用的音频文件都是Packed模式的。 FFmpeq解码不同格式的音频输出的音频采样格式不是一样。 其中AAC解码输出的数据为浮点型的 AV_SAMPLE_FMT_FLTP 格式&#xff0c;MP3 解码输出的数据为 AV_SAMPLE_FMT_S16P 格式(使用的…

如何开发无障碍的前端Web 网页

Web 无障碍设计&#xff08;Accessibility in Web design&#xff0c;也叫网站可及性 &#xff09;是要让所创建的网站对所有用户都可用/可访问&#xff0c;不管用户的生理/身体能力如何、不管用户是以何种方式访问网站。 让网页完全支持无障碍功能有一定成本&#xff0c;我们…

强化学习术语与超参数整理(PPO)

最近在isaac lab中使用各个强化学习框架做对比训练&#xff0c;算法都是用的PPO&#xff0c;但是每个框架里超参数名字都不太一样&#xff0c;各种叫法弄得都混乱了&#xff0c;而且对齐不好很难对比出结论&#xff0c;在这里系统整理一下。 Isaac Lab支持的强化学习框架介绍-…

fetchApi === 入门篇

目录 fetch 基本认知 fetch 如何使用 Response对象&#xff08;了解&#xff09; 常见属性 常见方法 fetch 配置参数 fetch发送post请求 fetch 函数封装 fetch 实战 - 图书管理案例 渲染功能 添加功能 删除数据 完整代码 fetch 基本认知 思考&#xff1a; 以前开发…

【文化+科技,融合示范】探索数字媒体产业园区如何重塑行业格局,打造全球领先的数字内容生态圈

数字媒体产业园区作为数字经济的重要组成部分&#xff0c;正以其独特的魅力和创新力重塑行业格局&#xff0c;努力打造全球领先的数字内容生态圈。这一过程中&#xff0c;树莓集团正通过集聚优质资源、搭建创新平台、推动产业升级&#xff0c;为数字内容产业的发展提供了强有力…

将git默认的编辑器设置为vin

git默认编辑器现状 如下&#xff0c;很多linux发行版&#xff0c;未加修改的情况下&#xff0c;git的默认编辑器使用起来不太方便 Signed-off-by: root <rootxxx.COM># Please enter the commit message for your changes. Lines starting # with # will be ignored, a…

MongoDB教程(二十三):关于MongoDB自增机制

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

[前端]解决Iframe即使设置高度100%,但还是显示滚动条scrollbar的问题

前言 好烦,你看看这两个重复的滚动条. 一个是来自iframe,另一个来自父级的div(overflow: auto;) 我已经在css中设置了iframe的height: 100%;border: none;,但无论如何还是显示出了父级的scrollbar 解决 将iframe的display: block;即可. 或者vertical-align: bottom;