串联所有单词的子串,找到所有符合条件的串联子串的起始索引
面试学习
一、题目
串联所有单词的子串
二、解题思路
2.1 定义子串长度
所有字符串 words 的长度是相同的,假设为 L。那么一个有效的串联子串的总长度应该是 L * len(words)。
2.2 滑动窗口
我们可以使用滑动窗口的方法来遍历字符串 s。窗口的大小应为 L * len(words)。在每个窗口内,我们检查是否可以找到 words 中所有字符串的一个排列。
2.3 哈希表
我们可以使用哈希表来记录 words 中每个字符串出现的次数,并使用另一个哈希表来记录当前窗口中每个字符串的出现次数。
2.4 验证子串
对于每个窗口,检查窗口中字符串的出现次数是否与 words 中字符串的出现次数一致。如果一致,则记录窗口的起始索引。
三、代码实现
// Helper function to check if two hash tables are equal int areHashTablesEqual(int *hash1, int *hash2, int size) { for (int i = 0; i < size; i++) { if (hash1[i] != hash2[i]) { return 0; } } return 1; } void findSubstring(char *s, char **words, int wordsSize, int wordLen, int *result, int *returnSize) { int sLen = strlen(s); int concatLen = wordsSize * wordLen; int *wordCount = (int *)calloc(256, sizeof(int)); int *windowCount = (int *)calloc(256, sizeof(int)); // Initialize wordCount hash table for (int i = 0; i < wordsSize; i++) { for (int j = 0; j < wordLen; j++) { wordCount[(unsigned char)words[i][j]]++; } } for (int i = 0; i <= sLen - concatLen; i++) { memset(windowCount, 0, 256 * sizeof(int)); int j; for (j = 0; j < wordsSize; j++) { char *word = s + i + j * wordLen; for (int k = 0; k < wordLen; k++) { windowCount[(unsigned char)word[k]]++; } } if (areHashTablesEqual(wordCount, windowCount, 256)) { result[(*returnSize)++] = i; } } free(wordCount); free(windowCount); } int* findSubstringIndices(char *s, char **words, int wordsSize, int *returnSize) { int wordLen = strlen(words[0]); int *result = (int *)malloc(sizeof(int) * 1000); // Allocate enough space for result *returnSize = 0; findSubstring(s, words, wordsSize, wordLen, result, returnSize); return result; }
四、代码讲解
4.1 areHashTablesEqual
检查两个哈希表是否相等。
4.2 findSubstring
主要逻辑,包括:
- 初始化哈希表 wordCount 记录 words 中所有字符串的出现次数。
- 使用滑动窗口方法检查每个窗口内的字符出现次数是否匹配 wordCount。
- 如果匹配,将窗口的起始索引存入结果数组。
4.3 findSubstringIndices
封装 findSubstring 函数,返回结果数组。