1.题目
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
2、实现
Java
数据结构:hashmap,key的唯一性
思路:滑动窗口 ,map的(key,value)存放string s的(s.charAt(i),i),map的key是唯一的,但是put相同key的value1,会更新。用start表示字符串的开始位置,i为当前游标,如果当前字符 s.charAt(i) 已经存在map的keySet中,并且当前的start不大于s.charAt(i) 对应的map中存在的之前添加的Preindex (map.get(s.charAt(i))),就把start滑动到Preindex后一位
即,如果 s[i]在 [start ,i) 范围内有与之重复的字符s[i′ ], 我们直接跳过 [start,i'] 范围内的所有元素,并将 start 变为 i' + 1
写法一:增加显示判断 strat<=i' ,并且只有在start改变时,ans才会改变,才需要max对比当前和之前的长度,这个方法提交后执行效率都优于以下其他写法
class Solution {
public int lengthOfLongestSubstring(String s) {
int start=0,ans=0;
Map<Character,Integer> map=new HashMap<>();
for(int i=0;i<s.length();i++){
if(map.containsKey(s.charAt(i))&&start<=map.get(s.charAt(i))){
start=map.get(s.charAt(i))+1;
}else{
ans=Math.max(ans,i-start+1);
}
map.put(s.charAt(i),i);
}
return ans;
}
}
写法二: start更新时直接用max计算
class Solution {
public int lengthOfLongestSubstring(String s) {
int start=0,ans=0;
Map<Character,Integer> map=new HashMap<>();
for(int i=0;i<s.length();i++){
if(map.containsKey(s.charAt(i))){
start=Math.max(map.get(s.charAt(i))+1,start);
}
ans=Math.max(ans,i-start+1);
map.put(s.charAt(i),i);
}
return ans;
}
}
写法三:加边界条件
class Solution {
public int lengthOfLongestSubstring(String s) {
int n=s.length();
if (n==0||n==1){
return n;
}
int start=0,ans=0;
Map<Character,Integer> map=new HashMap<>();
for(int i=0;i<n;i++){
if(map.containsKey(s.charAt(i))&&start<=map.get(s.charAt(i))){
start=map.get(s.charAt(i))+1;
}else{
ans=Math.max(ans,i-start+1);
}
map.put(s.charAt(i),i);
}
return ans;
}
}
Python
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if len(s)=="":return 0
if len(s)==1:return 1
d_map = {}
start = ans = 0
for i in range(len(s)):
if s[i] in d_map and start<=d_map[s[i]]:
start = d_map[s[i]] + 1
else:
ans = max(ans, i - start + 1)
d_map[s[i]] = i
return ans
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if len(s)=="":return 0
if len(s)==1:return 1
d_map = {}
start = ans = 0
for i in range(len(s)):
if s[i] in d_map:
start = max(d_map[s[i]] + 1,start)
ans = max(ans, i - start + 1)
d_map[s[i]] = i
return ans
边界判断换成 for in 执行时间就边长了,虽然写法很python,但效率却不如显式直接判断
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
n=len(s)
if n in (0,1):
return n
d_map = {}
start = ans = 0
for i in range(n):
if s[i] in d_map and start<=d_map[s[i]]:
start = d_map[s[i]] + 1
else:
ans = max(ans, i - start + 1)
d_map[s[i]] = i
return ans
3、本题中语言点
set 和第2第中用到的一样
4、就提交效率对比,显式条件判断,比调用函数执行效率高
if len(s)=="":return 0 if len(s)==1:return 1 | if n in (0,1): return n |
if(map.containsKey(s.charAt(i))&&start<=map.get(s.charAt(i))){
| if(map.containsKey(s.charAt(i))){ start=Math.max(map.get(s.charAt(i))+1,start); } |
后记:
后来突然想明白一个问题:这里的判断start<=map.get(s.charAt(i)) ,其实是使用map数据结构导致的影响。
单纯考虑到滑动窗口,只会想到窗口沿找index增大的方向往后滑动,但是当用map一直在存数据的时候,滑动窗口开始位置start之前的那些曾经放入map的元素并没有从map中删除过,一直存在map里面,所以有可能会start>map.get(s.charAt(i))。因为我们窗口要向后滑动,所以这里必须要加条件限制start<=map.get(s.charAt(i))