给定一个字符串 s ,请你找出其中不含有重复字符的 最长
子串
的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104s由英文字母、数字、符号和空格组成
因为s由英文字母、数字、符号和空格组成,符合ASCII码,故采用ASCII码大小的数组,使用ASCII码为下标,记录每个字符出现的最后位置。并在每次循环的开始,将left更新为上一次该字符出现的位置+1。
滑动窗口算法的基本原理:
-
初始化窗口:定义两个指针,通常是
left和right,并初始化为字符串或数组的起始位置。 -
滑动窗口:
right指针逐个增加,尝试扩大窗口,同时更新解的答案。 -
更新解的答案:当满足题目条件时,我们需要更新当前的最优解。
-
收缩窗口:如果当前窗口不满足题目的条件,移动
left指针缩小窗口,直到满足条件为止。
示例分析:
以题目"abcabcbb"为例,详细步骤如下:
-
初始化:
left = 0, right = 0, maxLen = 0, charIndex[] = {-1, -1, -1, ...} -
right逐个增加:s[0] = 'a',charIndex['a'] = -1,更新为charIndex['a'] = 0,窗口长度为1,maxLen = 1。s[1] = 'b',charIndex['b'] = -1,更新为charIndex['b'] = 1,窗口长度为2,maxLen = 2。s[2] = 'c',charIndex['c'] = -1,更新为charIndex['c'] = 2,窗口长度为3,maxLen = 3。s[3] = 'a',charIndex['a'] = 0,左指针移动到1,窗口变为"bca",窗口长度为3,maxLen保持不变。s[4] = 'b',charIndex['b'] = 1,左指针移动到2,窗口变为"cab",窗口长度为3,maxLen保持不变。s[5] = 'c',charIndex['c'] = 2,左指针移动到3,窗口变为"abc",窗口长度为3,maxLen保持不变。s[6] = 'b',charIndex['b'] = 1,左指针移动到4,窗口变为"bca",窗口长度为3,maxLen保持不变。s[7] = 'b',charIndex['b'] = 1,左指针移动到5,窗口变为"cab",窗口长度为3,maxLen保持不变。
最终结果为3。
int lengthOfLongestSubstring(char *s) {// 使用一个数组来存储每个字符最后出现的位置int charIndex[128]; // 假设字符集为ASCII,有128个字符memset(charIndex, -1, sizeof(charIndex)); // 初始化为-1int left = 0; // 左指针,用于标记窗口的开始位置int maxLen = 0; // 最长子串的长度int len = strlen(s);for (int right = 0; right < len; right++) {// 如果字符已经在窗口中,并且其位置在左指针之后if (charIndex[s[right]] >= left) {// 移动左指针到重复字符的下一个位置left = charIndex[s[right]] + 1;}// 更新每个字符的位置,每个字符初始left都是0charIndex[s[right]] = right;// 计算当前窗口的长度.正常是位置,如果该字符已经出现过了,就会更新left,减去的就是上一次出现的位置。int windowLen = right - left + 1;// 更新最长子串的长度if (windowLen > maxLen) {maxLen = windowLen;}}return maxLen;
}
right指针逐个增加,尝试扩大窗口。- 检查新加入的字符
s[right]是否已经在当前窗口中。 - 如果已经在窗口中并且其位置在
left指针之后,更新left指针的位置。 - 更新
charIndex数组中s[right]字符的位置为right。 - 计算当前窗口的长度
windowLen,并与maxLen比较,更新最长子串的长度。
