leetcode打卡10:题号:3 无重复字符的最长子串,给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
题目:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
题目解析一(暴力解法):
1.暴力解法其实就是不断的取子串进行判断是否有重复字符
2.所以标准答案给出了一个方案,定义一个allUnique(s,i,j)方法去判断在s中截取i到j的子串中是否存在重复
3.但是实际上这种做法非常的耗时,并且在leetcode上运行会报运行时间过长的问题
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0;
for (int i = 0; i < n; i++)
for (int j = i + 1; j <= n; j++)
if (allUnique(s, i, j)) ans = Math.max(ans, j - i);
return ans;
}
public boolean allUnique(String s, int start, int end) {
Set<Character> set = new HashSet<>();
for (int i = start; i < end; i++) {
Character ch = s.charAt(i);
if (set.contains(ch)) return false;
set.add(ch);
}
return true;
}
}
题目解析二(自己的算法):
1.这个方法是我自己写出来的,写之前我也不知道其实这道题最优的解法是使用滑动窗口,但是我写出来之后发现我的这种方法集成了暴力算法和滑动窗口,虽然不如滑动窗口,但是比暴力解法好多了
2.设置两个初始化索引 i=0,j = i + 1,每次先将s【i】传入hashmap(实际上使用hashset就可以),之后开始从s【j】判断map中是否存在重复字符,如果不重复,就将s【j】传进去(其实就是判断从i到j的窗口中是否重复),然后j++
3.如果发现s【j】已经重复了,那么就记录窗口大小,然后 i++ ,j = i+1;这样就又重新开始了新的一遍遍历,第二次遍历就从i = 1, j=2,j开始往后走开始遍历。
public static int lengthOfLongestSubstring(String s) {
if(s.equals("") ) return 0;
// if(s.length() == 0) return 0;
int i = 0;
int j = 0;
int result = 1;
for(i = 0; i < s.length(); i++){
int temp = 1;
HashMap map = new HashMap();
map.put(s.charAt(i),i);
for(j = i + 1; j < s.length(); j++){
if(!map.containsKey(s.charAt(j))){
map.put(s.charAt(j),j);
temp = temp + 1;
}else{
break;
}
}
if(temp > result) result = temp;
}
return result;
}
题目解析三(滑动窗口解法)
1.这个解法其实就是我自己写的方法的优化解法,我刚刚的解法还是可以优化的,具体优化的地方就在于,我每次的窗口是[i,j),每次走到重复的地方跳出,我又重新将窗口变成了[i+1,i+2),到这里我又要重新将map清空,然后又要从s【i+1】开始往map里面存值,但是实际上刚刚的清空的map中都会已经存有从s【i+1】开始的值,这样空间上和时间上都浪费了
2.所以我的窗口从[i,j)找到了重复的地方时,为了避免将set(这里使用set)清空,我将窗口直接变成[i+1,j),将set中的s【i】取出,这样的话就节省了我重新从s【i+1】开始存储的时间。
public int lengthOfLongestSubstring1(String s) {
if(s.equals("") ) return 0;
int i = 0;
int j = 0;
int length = s.length();
int result = 0;
HashSet set = new HashSet();
while(i < length && j < length ){
if(!set.contains(s.charAt(j))){ //无重复的就存储 区间右端点右移
set.add(s.charAt(j));
j += 1;
result = Math.max(result,j-i);//取区间长度
}else{//重复了 区间左端点右移
set.remove(s.charAt(i));
i += 1;
}
}
return result;
}
这里就比我刚刚的简单多了,省了每个新遍历的存储时间和判断时间。
题目分析四(最优化解法):
1.对于第三解法的滑动窗口解法如果已经弄懂了话,那么其实我们很清楚,当我们在[i,j)中找到了 s【j’ 】与 s【j】相同的话,我们其实可以直接将窗口定位到[j’+1,j),为什么这么做呢,因为 如果用上面的方法,[i,j’]中的元素我们还要一直和s[j]进行判断,不断的将区间左端点右移,直到移到j’+1,所以我们完全可以用空间换时间的想法,直接用索引定位 j’ 的位置,直接将区间定位到[j’+1,j)
2.但是在这里我们用的是map,并且不会进行擦除元素,也就是说当我们定位到[j’+1,j)之后,后面的字符在判断重复的时候可能会判断到区间之前的元素,这里要进行一个判断即可
public int lengthOfLongestSubstring4(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;
}
总结:
1.String字符串取长度的方法是length();
2.字符串判断为空串是用equals,用==判断不准,在我的IDEA中是可以的,但是在leetcode上不行
3.如果字符不为空,那么最小长度为1
4.hashset的判断是否存在方法为contains
5.取字符串的字符用charAt方法
6.map的get方法取到的是object对象,要进行强制转换
上一篇: 成功运营网站的11要素小结
下一篇: 浅析地方门户网站内容建设与推广三要素