无重复字符的最长子串
- 题目
- 函数原型
- 边界判断
- 算法设计:双指针实现滑动窗口
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
函数原型
C
的函数原型:
int lengthOfLongestSubstring(char * s){}
边界判断
int lengthOfLongestSubstring(char * s){
if(s == NULL || *s == '\0')
return 0;
}
算法设计:双指针实现滑动窗口
思路:找一个范围使得其值满足某个条件,然后就会想到滑动窗口,也就是用双指针的方法。
- 用俩个指针
i
和j
表示一个窗口 - 记录此时的长度,
i
向右移动,开始减少长度,每减少一次,就更新最小长度 - 直到当前窗口内的数字和小于了
,回到第
比如,现在看到这样一个子数组:
为了寻找最长的子串,就要往后多看一个数据。
检查当前获取的字符,与之前的子串是不是产生了重复字符。如果没有产生,右指针j++
。
此时,找到一个更长的不重复子串,即 。
而后,依此类推。尝试增大 右指针j
寻找更长的没有重复字符的子串。
直到,要寻找的下一个与子串产生了重复的字符时,就无法继续扩展了。
这时候,开始收缩。左指针i
移动到重复字符的右边。
此时,右指针j++
,又可以继续扩展了,因为前面的重复字符已经丢出来了,找出最长的字串为止。
有一个问题,如何检查重复的字符呢?
顺序扫描 ,一般查找要优化到
。
可以用一个数组 c[256]
记录字符出现的频率, 包含了存储
#define max(x, y) ((x)>(y)?(x):(y))
int lengthOfLongestSubstring(char * s){
if(s == NULL || *s == '\0')
return 0;
int *c = calloc(128, sizeof(int));
int ans = 0;
// s[i...j], 控制滑动窗口的双指针
int i = 0;
int j = -1;
int len = strlen(s);
while( i < len ){
// 获取新窗口
if( c[ s[j+1] ] == 0 ) // 没有出现过,即当前字符没有重复
c[ s[++j] ] ++; // 扩展右边界
else
c[ s[i++] ] --; // 收缩左边界,左指针i 移动到重复字符的右边,每次加一有点慢,最好是瞬移过去
ans = max(ans, (j - i + 1));
}
free(c), c = NULL;
return ans;
}
提交上去,发现数组越界访问了。
想一下,就知道是 右指针j
了,所以加个判断防止越界访问。
if( c[ s[j+1] ] == 0 && j+1 < len )
完整代码:
#define max(x, y) ((x)>(y)?(x):(y))
int lengthOfLongestSubstring(char * s){
if(s == NULL || *s == '\0')
return 0;
int *c = calloc(128, sizeof(int));
int ans = 0;
// s[i...j], 控制滑动窗口的双指针
int i = 0;
int j = -1;
int len = strlen(s);
while( i < len ){
// 获取新窗口
if( c[ s[j+1] ] == 0 && j+1 < len ) // 没有出现过,即当前字符没有重复
c[ s[++j] ] ++; // 扩展右边界
else
c[ s[i++] ] --; // 收缩左边界,左指针i 移动到重复字符的右边,每次加一有点慢,最好是瞬移过去
ans = max(ans, (j - i + 1));
// 比较最大无重复子串的长度这一步,只需扩展右边界时比较, 收缩左边界不用比较
// 所以,可以写在 if 语句的 c[ s[++j] ] ++ 下,避免不必要的计算
}
free(c), c = NULL;
return ans;
}
- 时间复杂度:
- 空间复杂度: