0
点赞
收藏
分享

微信扫一扫

[LeetCode]Longest Substring Without Repeating Characters

elvinyang 2023-02-02 阅读 111


Question:
Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.

Given “bbbbb”, the answer is “b”, with the length of 1.

Given “pwwkew”, 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.

遇到的问题:
1、题目要求没看清。
一开始大概扫了一下题目,以为是返回最长字符串,在coding是否居然还未察觉,不看返回类型,结果写到return时候直接蒙圈了。

2、测试用例考虑不够。
只考虑了:ecaba,并未考虑ca这种,更不必谈其他。导致返回的max不是最大值,最大值在现在的hashtable未赋值给max,结果就是fail。(解决的办法就是贪婪,每次只要没有重复字符,就给max做赋值操作)

3、应该简化代码。
比如以下代码,孰好孰坏一眼便知:

if(char_map.size()>ans)
ans = char_map.size();

ans = Math.max(char_map.size(),ans);

下面是我的代码:

public class Solution {
public int lengthOfLongestSubstring(String s) {
//require
if(s==null||s.length()==0)
return 0;
// throw IllegalArgumentException("s cannot be null.");
int head = 0;
int max = 0;
Map<Character,Integer> char_map = new HashMap<>();
Map<Integer,Character> int_map = new HashMap<>();
//string->char[]
char[] chars=s.toCharArray();
//invariant
for(int i=0;i<s.length();i++){
if(char_map.containsKey(chars[i])){
//get the last string size
int last= i-head;
if(last>max)
max=last;
//cut off the substring from head to repeat position
int r = char_map.get(chars[i]);
for(int j=r;j>=head;j--){
char delete_char=int_map.get(j);int_map.remove(j);
char_map.remove(delete_char);
}
head=r+1;
}
char_map.put(chars[i],i);
int_map.put(i,chars[i]);
}
//ensure
if(char_map.size()>max)
max=char_map.size();
return max;
}
}

思路:
1、保证char_map中始终不能有重复的char,当遇到重复的char时,该char的位置之前的char全部都要从char_map中清除出去。
例如ecabaf,当map中含有ecab时,如果这时判断是第5个字符a,那么不仅要把ecab中的a清除掉,而且ec都要从map中清除。
2、在substring结束时(遇到重复char),才进行测算其长度并与max比较。

下面是参考答案1:

public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
}

它的思路:
1、贪婪。每次没遇到重复char,都要计算长度并与ans进行比较(比我的好,保证最后的ans一定是最大值)
2、i和j就像一条蚯蚓的头和尾,一点点往前拱。效率低下不言而喻。

下面是改进的算法:

public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}

它的思路我很惭愧地看了近20分钟才想明白。原因就是我总是按我的思路套它的思路,这是不可能很好理解的。最好的办法就是放下你的思路,手动simulate它的过程,你自然理解。
它的思路与我的大体相当,所不同就是在于它的map中允许有重复的char之前的char,而我的不允许(我的思路1)。其实是我没搞明白解决这个问题的核心:map里面可以有重复char之前的char,只要让它作废即可。这个算法的巧妙就在于用了:

i = Math.max(map.get(s.charAt(j)), i);

让substring的头贴在最后的重复char后,这样等同于让之前的char作废(如果之前的char与新的char不重复,当然皆大欢喜不影响结果;如果重复,也不影响结果)。这就是我纠结所在。

更牛的是这个:

public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}
}

就是用数组代替了hashmap,不过有Assumption:
If we know that the charset is rather small, we can replace the Map with an integer array as direct access table.


举报

相关推荐

0 条评论