您的位置:首页 > 其它

5/2 LeetCode每日一题 3. 无重复字符的最长子串

2020-06-03 05:17 218 查看

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1: 输入: “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2:
输入: “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示例 3: 输入:
“pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。 请注意,你的答案必须是 子串
的长度,“pwke” 是一个子序列,不是子串。

每天同步在公众号 算法学习笔记 中更新,欢迎各位关注!
分析:

这是一道非常经典的双指针法中的滑动窗口问题。

解法1:暴力法

首先很容易想到暴力法:设置首尾两个指针 i , j :

for (i in s)
for (j in s[i:])
for (k in s[i:j])
for (l in s[k:j])
if (s[k] == s[l])
len=1;
else
len++;
res = max(len,res);

时间复杂度。。。爆炸了;空间复杂度 O(1)

那么稍微聪明点的做法就是把有无重复元素判断封装起来,用桶或者哈希表之类的方式实现。

for (i in s)
for (j in s[i:])
if (s[i:j]中无重复元素)
res = max (res,j-i+1)

但是时间复杂度还是O(n²)

对于这类线性结构的问题,我们应该尽可能想办法把O(n²)的时间复杂度降为O(nlogn)甚至更低。

解法2:滑动窗口+哈希桶/哈希表

在上述解法中,计算机做了大量重复而无用的计算和判断工作。比如:在判断s[i+1,j]中是否存在重复字符和s[i,j]是否存在重复字符这二者的工作中,这种算法二者之间没有联系起相互之间的关系。

以[ i , j ]递推到[ i , j+1 ]为例:我们大可不必先用O(n)时间先证明一次[ i , j ]中有无重复元素,再用O(n)时间证明一次[ i , j+1 ]有无重复,这样是浪费算力。

如果[ i , j ]中无重复元素的话,我们可以将[ i , j ]范围内所有的字符值记录在某个容器中,再判断s[j+1]是否在容器中即可。

如果[ i , j ]中有重复元素的话,把s[i]对应的键值减少1,再让i++,判断容器中所有的字符是否唯一。若不唯一,重复该操作,直到唯一为止。
这就是滑动窗口(Sliding Windows)的核心思想,它也可以理解为 DP的一种形式。LeetCode上有相当多的题目都是这种套路。用伪码来描述就是

l = 0, r = 1 ,s[l] s[r]都丢入容器
for (;r < s.len;r++)
while (s[r+1]在容器中)
容器中的s[l]--
l++
r++
将s[r]丢入容器
res = max(res,r-l+1)

这里的算法利用了前期计算的结果作为基础,复杂度大大下降,成为线性的O(n),完全可以被接受了。
具体代码实现

class Solution {
public:
inline int max(int a, int b) { return a > b ? a : b; }
int lengthOfLongestSubstring(string s) {
int left = 0, right = 0;
int len = s.length() - 1;
if (len == -1)  return 0;
int res = 1;
if (len == 0) return 1;//预处理一哈
bool bucket[128] = { false };//CHAR_MAX是127
bucket[s[0]] = 1;
while (right < len)
{
while (bucket[s[right + 1]])
{
bucket[s[left++]] = 0;//清空桶
}
right++;
bucket[s[right]] = 1;//把右边的值丢入桶
res = max(res, right - left + 1);
}
return res;
}
};

解法3:哈希表记录前缀值

还可以有别的写法吗?我不想嵌套内层的while循环。

当然可以
利用一个hashmap来存放每个字母最新出现的位置。若hashMap中显示已访问该值,则更新左值。

class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> m(128, -1);
int res = 0, left = -1;
for(int i=0;i<s.length();i++){
left = max(m[s[i]], left);//若已经访问过这个点就更新左值
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};

时间复杂度同样是O(n)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: