438.找到字符串中所有字母异位词(滑动窗口)

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

 示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • s 和 p 仅包含小写字母

解题思路

  1. 统计字符频率

    • 使用一个长度为 26 的数组 p_count 记录字符串 p 中每个字符的出现次数。

    • 比如,p = "abc",则 p_count = [1, 1, 1, 0, ..., 0]'a' 出现 1 次,'b' 出现 1 次,'c' 出现 1 次)。

  2. 滑动窗口

    • 在字符串 s 上滑动一个长度为 len(p) 的窗口,统计窗口内每个字符的出现次数。

    • 使用另一个长度为 26 的数组 window_count 记录当前窗口内字符的出现次数。

    • 比较 window_count 和 p_count,如果相等,则当前窗口是一个异位词,记录窗口的起始索引。

写的挺狗史的,但也记录一下把

int* findAnagrams(char* s, char* p, int* returnSize) 
{
    int len_p = strlen(p);
    int len_s = strlen(s);
    *returnSize = 0;  // 初始化返回地址的大小为0
    
    // 情况1:当主串S长度小于对比串P,不可能有异位词,返回空
    if(len_s<len_p)
    {
        return NULL;
    }

    // 正常情况:
    // 初始化计数数组
    int p_count[26]={0};  // 用于记录p中每个字符出现的次数
    int window_count[26]={0};  // 记录当前窗口每个字符出现的次数

    // 填充P的次数
    for(int i=0; i<len_p; i++)
    {
        char current_char = p[i];  // 获取当前字符存到p[i]
        int index = current_char - 'a';  // 字符转换为索引值
        p_count[index]++;  // 对应的索引值+1
    }

    // 预分配结果数组,最大的可能为len_s-len_p+1
    int* result = (int*)malloc(sizeof(int)*(len_s-len_p+1));  //  指针自身 = (指针类型*)malloc(sizeof(指针类型)*数据数量)
    if (result == NULL)  // 检查是否成功分配内存,如果分配失败,返回NULL
    {
        *returnSize = 0;
        return NULL;
    }

    // 窗口滑动处理
    for(int i=0; i<=len_s-len_p; i++)
    {
        // 重置当前窗口计数
        for(int k=0; k<26; k++)
        {
            window_count[k]=0;
        }
        // 填充当前窗口计数
        for(int j=0; j<len_p; j++)
        {
            char current_char_w = s[i+j]; // 获取当前字符
            int index_w = current_char_w - 'a';  // 将字符转换为索引
            window_count[index_w]++;
        }
        // 比较当前窗口是否与p的计数匹配
        int is_match = 1;  // 先假设匹配
        for(int k=0; k<26; k++)
        {
            if(p_count[k]!=window_count[k])
            {
                is_match = 0;  // 不匹配
                break;
            }
        }
        // 如果匹配,记录起始索引
        if(is_match)
        {
            // 将当前窗口的起始索引 i 存入结果数组
            result[*returnSize] = i;
            // 更新结果数组的大小,加 1
            *returnSize = *returnSize + 1;
        }
    }
    return result;
}

滑动窗口部分:

遍历长字符串S,从每个位置都扫一遍长度等于短串p的字串;重置窗口,统计当前窗口的字符频率;比较window_count和p_count,如果相等,则记录起始索引i

GPT帮忙写个模拟过程,好理解

举例:

模拟过程

输入:
s = "cbaebabacd";
p = "abc";
步骤:
  1. 初始化

    • len_p = 3len_s = 10

    • p_count = [1, 1, 1, 0, ..., 0]'a''b''c' 各出现 1 次)。

  2. 滑动窗口

    • 窗口 1i = 0,子串 "cba"

      • window_count = [1, 1, 1, 0, ..., 0]

      • 与 p_count 匹配,记录索引 0

    • 窗口 2i = 1,子串 "bae"

      • window_count = [1, 1, 0, 0, ..., 0]

      • 不匹配。

    • 窗口 3i = 2,子串 "aeb"

      • window_count = [1, 1, 0, 0, ..., 0]

      • 不匹配。

    • 窗口 4i = 3,子串 "eba"

      • window_count = [1, 1, 0, 0, ..., 0]

      • 不匹配。

    • 窗口 5i = 4,子串 "bab"

      • window_count = [0, 2, 0, 0, ..., 0]

      • 不匹配。

    • 窗口 6i = 5,子串 "aba"

      • window_count = [2, 1, 0, 0, ..., 0]

      • 不匹配。

    • 窗口 7i = 6,子串 "bac"

      • window_count = [1, 1, 1, 0, ..., 0]

      • 与 p_count 匹配,记录索引 6

  3. 结果

    result = [0, 6]

总结

  • 滑动窗口:通过滑动窗口统计字符频率,判断是否与 p 的字符频率匹配。

  • 时间复杂度:O(n * m),其中 n 是 s 的长度,m 是 p 的长度。

  • 空间复杂度:O(1),使用了固定大小的计数数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值