欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > 力扣第三十题——串联所有单词的子串

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

2024/11/30 11:33:19 来源:https://blog.csdn.net/m0_74932528/article/details/140634462  浏览:    关键词:力扣第三十题——串联所有单词的子串

内容介绍

给定一个字符串 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. 及时清理:在每次外层循环结束后清理哈希表,避免内存泄漏。

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com