题目:3. Longest Substring Without Repeating Characters 题目连接
题目描述:
Given a string, find the length of the longest substring without repeating characters.
Example 1:
Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:
Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
题意:
这道题 题意非常的简单明了即 给一个字符串,输出这个字符串不包含重复字符的最长字串长度。
分析:
- 第一种思路:最直接的是使用类似字符串匹配的思想来做,这种方法简单易懂,但时间复杂度极高。实现如下code1:
- 第二种思路:参考第一种方法可知,其中包含了很多不必要的循环和查找,如何来简化呢? 可分以下几个步骤:
- 使用unordered_set (无序集合容器) 来存储字符串中唯一出现过的字符。
- 顺序遍历字符串,且当 当前字符在容器中不存在时继续往后遍历,否则终止循环。
- 循环终止之后 更新不重复字串长度最大值,这时如果是因为循环到了字符串最后位置,则可跳出循环,完成查找。
- 当访问的字符已经在无序容器中时,终止上述循环,然后擦除头元素(因为 如果出现重复的字符,那么重复的字符肯定为头元素),指向最左侧的下表往后移一位。
- 完成查找,返回结果,实现代码见Code2。
- 第三种思路:在前面两种得到结果之后发现,算法执行的效率仍不理想,因此现在换用另外一种容器,尝试是否能够更加高效一些,这里使用了hash的概念。实现思路类同第二种思路, 代码实现见Code3。
Code1
class Solution {
public:
int lengthOfLongestSubstring(string str) {
int right=1,maxLength=0, len=str.size();
for(int i=0;i<len-1;++i){
string tmp = str.substr(i,1);
while( i+right<len && (tmp.find(str[i+right]) == string::npos)){
tmp += str[i+right];
++right;
}
if(tmp.size()>maxLength){
maxLength = tmp.size();
}
}
return maxLength;
}
};
结果:
Runtime: 140 ms, faster than 14.21% of C++ online submissions for Longest Substring Without Repeating Characters.
Memory Usage: 27 MB, less than 100.00% of C++ online submissions for Longest Substring Without Repeating Characters.
Code2
class Solution {
public:
int lengthOfLongestSubstring(string str) {
if(str.size()<1) return 0;
unordered_set<char> current_set;
int maxLength=0, start=0, index=0;
while(index < str.size()){
while(index<str.size() && current_set.find(str[index]) == current_set.end()){
current_set.insert(str[index]);
++index;
}
maxLength = maxLength>current_set.size()?maxLength:current_set.size();
if(index == str.size()){
break;
}else{
current_set.erase(current_set.find(str[start]));
++start;
}
}
return maxLength;
}
};
结果:
Runtime: 52 ms, faster than 30.02% of C++ online submissions for Longest Substring Without Repeating Characters.
Memory Usage: 18.9 MB, less than 100.00% of C++ online submissions for Longest Substring Without Repeating Characters.
对比以上两种方法 可以看出 在简化掉一些不必要的循环之后,执行速度快了将近三倍。
可以发现在以上优化之后faster than 30.02% of C++, 说明仍有很多需要优化的地方。接下来第三种方法使用hash继续简化。
Code3
vector<int> res(256,-1);
int maxLength=0,start=-1;
for(int i=0;i<str.length();++i){
if(res[str[i]]>start){
start = res[str[i]];
}
res[str[i]] = i;
maxLength = max(maxLength, i-start);
}
return maxLength;
使用更加简洁一点的vector实现hash的效果,可以看到简化之后代码也更加的清晰。
结果
Runtime:20 ms, faster than 98.96%of C++ online submissions for Longest Substring Without Repeating Characters.
Memory Usage: 16.2 MB, less than 100.00% of C++ online submissions for Longest Substring Without Repeating Characters.
现在可以看到,使用hash之后相比之前的unordered_set 执行速度又快了一倍。
Python
下面python按照上面一样的思路来实现。代码如下
class Solution:
def lengthOfLongestSubstring(self, s: 'str') -> 'int':
a = {}
maxlength=0
start=-1
index=0
for i,ch in enumerate(s):
if ch in a and a[ch] > start:
start = a[ch]
a[ch] = i
maxlength = max(i-start,maxlength)
return maxlength
结果:
Runtime: 72 ms, faster than 96.26% of Python3 online submissions for Longest Substring Without Repeating Characters.
Memory Usage: 12.6 MB, less than 100.00% of Python3 online submissions for Longest Substring Without Repeating Characters.
Status Accepted
下划线
补充一点点偶然见看到的小知识点(C++ string.size() 和 string.length()有什么区别呢):
我们都经常使用到string字符串,那么如何获取字符串的长度呢(比如在循环遍历字符串的每一个字符的时候),之前常用的方法是 这种方法。
string str;
int len = str.size();
今天偶然看到另一种很少用的方法:
string str;
int len = str.length();
那么,两者有什么区别呢?
先来看下官方文档怎么介绍的。
没错,以上就是C++官方对string::size 以及 string::length 的介绍,可以看到他们是完全等价的。两个函数返回值均为字符串的字节数。
那么,如此严谨的C++ 为什么就同一个功能维护两个函数呢?
因为,length主要是一开始C++为了和C兼容,而保留下来的。string类最初只有length,在加入STL之后,才加入了size,它也是作为STL的属性存在的。