题目

给定一个字符串 s 和一个单词数组 words,要求找到所有可以由单词数组中的单词串联而成的子串,并返回这些子串的起始索引。

引言

串联所有单词的子串问题需要考虑字符串的匹配和遍历的逻辑,以及单词数组的排列组合。我们可以使用滑动窗口的方法来解决这个问题,通过从字符串中截取长度与单词数组中所有单词长度之和相等的子串,并与单词数组进行匹配。解决这个问题需要对字符串进行滑动窗口操作和单词数组的排列组合。

算法思路

我们将使用滑动窗口的方法来解决串联所有单词的子串问题。

算法的步骤如下:

  1. 获取字符串 s 和单词数组 words 的长度,以及单词数组中单词的长度 wordLen
  2. 定义一个哈希表 wordCount,用于记录单词数组中每个单词的出现次数。
  3. 遍历字符串 s,通过滑动窗口的方式截取长度为 wordLen * wordsCount 的子串,其中 wordsCount 是单词数组中单词的数量。
  4. 将截取得到的子串划分为长度为 wordLen 的多个子串,并统计每个子串出现的次数,保存在哈希表 currWordCount 中。
  5. currWordCountwordCount 进行比较,如果两个哈希表相等,则说明截取得到的子串可以由单词数组中的单词串联而成,将子串的起始索引保存到结果数组中。
  6. 继续遍历字符串 s,直到所有可能的子串都被考虑过。
  7. 返回结果数组,即所有可以由单词数组中的单词串联而成的子串的起始索引。

代码实现

下面是使用C语言实现的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char* word;
    int count;
} WordInfo;

int compareWordInfo(const void* a, const void* b) {
    return strcmp(((WordInfo*)a)->word, ((WordInfo*)b)->word);
}

int* findSubstring(char* s, char** words, int wordsSize, int* returnSize) {
    int sLen = strlen(s);
    int wordLen = strlen(words[0]);
    int windowSize = wordLen * wordsSize;
    int resultSize = 0;
    int* result = NULL;

    // 创建并初始化单词数组的哈希表
    WordInfo* wordInfos = (WordInfo*)malloc(wordsSize * sizeof(WordInfo));
    for (int i = 0; i < wordsSize; i++) {
        wordInfos[i].word = words[i];
        wordInfos[i].count = 0;
    }

    // 将单词数组排序,方便后续比较
    qsort(wordInfos, wordsSize, sizeof(WordInfo), compareWordInfo);

    // 遍历字符串s,通过滑动窗口截取子串,并比较子串中的单词是否与单词数组匹配
    for (int i = 0; i < wordLen; i++) {
        int left = i;
        int right = i;
        while (right + wordLen <= sLen) {
            char* subStr = &s[right];
            WordInfo key = {subStr, 0};
            WordInfo* foundWord = (WordInfo*)bsearch(&key, wordInfos, wordsSize, sizeof(WordInfo), compareWordInfo);
            if (foundWord != NULL) {
                foundWord->count++;
                right += wordLen;
                while (foundWord->count > 1) {
                    char* removedWord = &s[left];
                    WordInfo key = {removedWord, 0};
                    WordInfo* removed = (WordInfo*)bsearch(&key, wordInfos, wordsSize, sizeof(WordInfo), compareWordInfo);
                    removed->count--;
                    left += wordLen;
                }
                if (right - left == windowSize) {
                    resultSize++;
                    result = (int*)realloc(result, resultSize * sizeof(int));
                    result[resultSize - 1] = left;
                    left += wordLen;
                }
            } else {
                // 如果子串中的单词不匹配,则将左指针右移,并将哈希表恢复为初始状态
                left = right + wordLen;
                for (int j = 0; j < wordsSize; j++) {
                    wordInfos[j].count = 0;
                }
                right = left;
            }
        }
    }

    free(wordInfos);

    *returnSize = resultSize;
    return result;
}

int main() {
    char s[] = "barfoothefoobarman";
    char* words[] = {"foo", "bar"};
    int wordsSize = sizeof(words) / sizeof(words[0]);
    int returnSize;

    int* result = findSubstring(s, words, wordsSize, &returnSize);

    printf("Original String: %s\n", s);
    printf("Words: ");
    for (int i = 0; i < wordsSize; i++) {
        printf("%s ", words[i]);
    }
    printf("\n");
    printf("Substrings' Starting Index: ");
    for (int i = 0; i < returnSize; i++) {
        printf("%d ", result[i]);
    }
    printf("\n");

    free(result);

    return 0;
}

算法分析

该算法需要对字符串 s 进行多次遍历,并在每次遍历时进行哈希表的操作和子串的比较,所以时间复杂度为 O(n m k),其中 n 是字符串 s 的长度,m 是单词数组中单词的数量,k 是单词的平均长度。

空间复杂度为 O(m),算法使用了一个大小为 m 的哈希表和一个大小为 m 的结果数组。

示例和测试

示例输入:

Original String: "barfoothefoobarman"
Words: foo bar

示例输出:

Substrings' Starting Index: 0 9

总结

本文使用C语言实现了解答串联所有单词的子串问题的代码。通过使用滑动窗口的方法,我们能够在给定字符串中找到所有可以由单词数组中的单词串联而成的子串,并返回这些子串的起始索引。该算法的时间复杂度为 O(n m k),空间复杂度为 O(m)。

标签: 编程算法, 编程算法题, 编程算法大全, 编程算法流程, 算法设计与分析, 数据结构与算法, 算法优化, 算法实现, 常见编程算法, 编程算法入门, 编程算法进阶, 编程算法精通