3.无重复字符的最长字串(滑动窗口+哈希)C语言

代码思路

1. 滑动窗口法

使用 滑动窗口法,通过维护一个窗口(由 start_index 和 end 定义),动态调整窗口的大小,确保窗口内的字符没有重复。

2. 哈希表记录字符位置
  • 使用一个数组 hash_map[128] 来记录每个字符最后一次出现的位置。

    • 数组大小为 128,因为 ASCII 字符的范围是 0 到 127

    • hash_map[c] 表示字符 c 最后一次出现的位置。

3. 滑动窗口的维护
  • start_index 表示当前窗口的起始位置。

  • end 表示当前窗口的结束位置。

  • 如果当前字符 s[end] 已经出现过,并且它的上一次出现位置在 start_index 之后,则更新 start_index 到 hash_map[s[end]],即跳过重复字符。

4. 更新最大长度
  • 每次更新窗口后,计算当前窗口的长度 end - start_index + 1,并更新 max_len

int lengthOfLongestSubstring(char* s) 
{
    int hash_map[128]={0};  // 用哈希表记录每个元素最后一次出现的位置,128是为了覆盖所有ASCII
    int max_len=0;   
    int start_index=0;
    for(int end=0; s[end]; end++)  // s[]是字符串的第n个字符,当end='\0'时结束循环
    {
        // s[end]传入字符,例如:字符串‘abcabcbb’
        // end=0,对于字符‘a’, s[end]='a',hash_map['a']=hash_map=[97]=0
        // 更新hash_map['a']=end+1=1,表示‘a’最后一次出现在索引0

        // 检查当前字符 s[end] 是否在当前窗口内重复出现。
        // 如果是,则更新窗口的起始位置 start_index。
        // 如果 map[s[end]] > start_index,说明字符 s[end] 在当前窗口内已经出现过。
        // 换句话说,字符 s[end] 上一次出现的位置在当前窗口的范围内。
        if(hash_map[s[end]]>start_index)  
        {
            start_index=hash_map[s[end]];
        }
        hash_map[s[end]]=end+1;
        // end-start_index+1:当前滑动窗口的长度
        max_len=fmax(max_len, end-start_index+1);
    }
    return max_len;
}

这道题很经典,用这道题来好好捋顺一下滑动窗口

关键点

  1. map[s[end]] 的作用

    记录字符 s[end] 最后一次出现的位置。
  2. map[s[end]] > start_index 的作用

    判断字符 s[end] 是否在当前窗口内重复出现。
  3. 更新 start_index 的作用

    跳过重复字符,确保窗口内的字符没有重复。

if (map[s[end]] > start_index) 的作用是检查当前字符是否在当前窗口内重复出现。如果是,则更新窗口的起始位置 start_index,确保窗口内的字符没有重复。这是滑动窗口算法的核心逻辑之一。

下面形象描述一过程

给定一个字符串 s = "abcabcbb",找到其中最长的无重复字符的子串。

滑动窗口的过程

我们用两个指针 start_index 和 end 来表示滑动窗口的起始和结束位置。初始时,start_index = 0end 从 0 开始向右移动。

初始状态
字符串: a b c a b c b b
索引:  0 1 2 3 4 5 6 7
遍历过程
1. end = 0,字符 'a'
  • 窗口:[a]

  • 窗口长度:0 - 0 + 1 = 1

  • max_len = 1

  • map['a'] = 1

窗口: [a] b c a b c b b
索引: 0
2. end = 1,字符 'b'
  • 窗口:[a, b]

  • 窗口长度:1 - 0 + 1 = 2

  • max_len = 2

  • map['b'] = 2

窗口: [a b] c a b c b b
索引: 0 1
3. end = 2,字符 'c'
  • 窗口:[a, b, c]

  • 窗口长度:2 - 0 + 1 = 3

  • max_len = 3

  • map['c'] = 3

窗口: [a b c] a b c b b
索引: 0 1 2
4. end = 3,字符 'a'
  • 字符 'a' 已经出现过,上一次出现的位置是 0

  • 更新 start_index = map['a'] = 1

  • 窗口:[b, c, a]

  • 窗口长度:3 - 1 + 1 = 3

  • max_len = 3

  • map['a'] = 4

窗口: a [b c a] b c b b
索引:    1 2 3
5. end = 4,字符 'b'
  • 字符 'b' 已经出现过,上一次出现的位置是 1

  • 更新 start_index = map['b'] = 2

  • 窗口:[c, a, b]

  • 窗口长度:4 - 2 + 1 = 3

  • max_len = 3

  • map['b'] = 5

窗口: a b [c a b] c b b
索引:      2 3 4
6. end = 5,字符 'c'
  • 字符 'c' 已经出现过,上一次出现的位置是 2

  • 更新 start_index = map['c'] = 3

  • 窗口:[a, b, c]

  • 窗口长度:5 - 3 + 1 = 3

  • max_len = 3

  • map['c'] = 6

窗口: a b c [a b c] b b
索引:        3 4 5
7. end = 6,字符 'b'
  • 字符 'b' 已经出现过,上一次出现的位置是 5

  • 更新 start_index = map['b'] = 6

  • 窗口:[c, b]

  • 窗口长度:6 - 6 + 1 = 1

  • max_len = 3

  • map['b'] = 7

窗口: a b c a b c [b] b
索引:            6
8. end = 7,字符 'b'
  • 字符 'b' 已经出现过,上一次出现的位置是 7

  • 更新 start_index = map['b'] = 8

  • 窗口:[b]

  • 窗口长度:7 - 7 + 1 = 1

  • max_len = 3

  • map['b'] = 8

窗口: a b c a b c b [b]
索引:              7

最终结果

  • 最大无重复字符子串的长度是 3

  • 对应的子串是 "abc"(从索引 0 到 2)或 "bca"(从索引 1 到 3)等。

通过上述过程,可以看到:

  1. 窗口的扩展

    end 指针向右移动,扩展窗口。
  2. 窗口的收缩

    当遇到重复字符时,start_index 指针向右移动,收缩窗口。
  3. 窗口长度的计算

    每次更新窗口后,计算当前窗口的长度 end - start_index + 1,并更新 max_len

总结

滑动窗口的核心思想是通过两个指针动态维护一个窗口,确保窗口内的字符没有重复,并记录最大长度。

下面是模板

int slidingWindow(char* s) 
{
    int map[128] = {0};  // 哈希表,记录字符的状态
    int start = 0;       // 窗口的起始位置
    int max_len = 0;     // 记录最大长度

    for (int end = 0; s[end]; end++)  // 遍历字符串
    {
        // 更新窗口的状态(例如:记录字符的出现次数或位置)
        map[s[end]]++;

        // 如果窗口不满足条件,则收缩窗口
        while (/* 窗口不满足条件 */)
        {
            map[s[start]]--;  // 更新窗口的状态
            start++;          // 收缩窗口
        }

        // 更新结果(例如:计算窗口的长度)
        max_len = fmax(max_len, end - start + 1);
    }

    return max_len;  // 返回结果
}

你可能感兴趣的:(哈希算法,算法,c语言,滑动窗口)