题目描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例1
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
第一次解法(Map):
我使用了Java中的Map,每次检测是否含有当前字符,若无则将当前字符添加进maps;若有,将当前字符串长度 cur_num +1。若当前长度大于最大长度,则记录最大长度。若最大长度大于剩余字符个数,直接退出循环。
向 HashMap 中添加元素后,元素的顺序会被改变,而 LinkedHashMap 则是按顺序放置的。
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> maps = new LinkedHashMap<Character, Integer>();
int max_num = 0, cur_num = 0;
int count = s.length();
if (count != 0) {
for (int i = 0; i < s.length(); i++) {//控制循环次数
maps.clear();
cur_num = 0;
int pos = 0;
for (int j = i; j < s.length(); j++) {
if (maps.containsKey(s.charAt(j))) {
break;
} else {
maps.put(s.charAt(j), pos);
pos++;
cur_num++;
}
}
if (cur_num > max_num) {
max_num = cur_num;
}
if (max_num >= (s.length()-i-1)) {
break;
}
}
}
return max_num;
}
}
第二次解法(滑动窗口)
Java集合类Set
Set 是一种无序、可重复的数据容器,与 List 一样继承于 Collection 接口,HashSet 和 TreeSet,这两种 Set 是日常工作中用的比较多的。
HashSet 和 TreeSet 增删改查操作一样
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
//System.out.println();
String a_str = "Java", b_str = "C++", c_str = "Python", d_str = "C";
Set<String> set = new HashSet<>();
set.add(a_str); //增
set.add(b_str);
set.add(c_str);
set.add(d_str);
for (String value : set) { //查
System.out.print(value + " ");
}
System.out.println();
set.remove(a_str); //删
//改: java的HashSet文档貌似没有提供直接的修改操作。间接的做法是先删后增
for (String value : set) { //查
System.out.print(value + " ");
}
}
}
Set常见方法
Sr.No. | Method & Description |
---|---|
1 | add( ) 向集合中添加元素 |
2 | clear( ) 去掉集合中所有的元素 |
3 | contains( ) 判断集合中是否包含某一个元素 |
4 | isEmpty( ) 判断集合是否为空 |
5 | iterator( ) 主要用于递归集合,返回一个Iterator()对象 |
6 | remove( ) 从集合中去掉特定的对象 |
7 | size( ) 返回集合的大小 |
本题实现代码
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int max_num = 0;
int i = 0, j = 0;
int len = s.length();
while (i < len && j < len) {
if (! set.contains(s.charAt(j))) {
set.add(s.charAt(j++));
max_num = Math.max(max_num, j-i);
} else {
if (max_num >= (len-i-1)) {
break;
}
set.remove(s.charAt(i++));
}
}
return max_num;
}
}
我所理解的滑动窗口:比如一个字符串" pwwkew ",将字符一个一个放进窗口(相当于一个队列)中。
- p —— max_num = 1
- p,w —— max_num = 2
- p,w,w —— max_num = 2(这时候已有一个w在队列中,这是需要将前面个w及其之前的所有元素移出队列),变为 w —— max_num = 2
- w,k —— max_num = 2
- w,k ,e —— max_num = 3
- w,k ,e,w —— max_num = 3(这时将前面个w移出队列),变为 k ,e —— max_num = 3
(而在第五步时,i = 2, s.length() = 6, 此时 max_num = 3, max_num 大于等于后续未检测到的剩余字符总数(s.length() - i - 1),那么就不用继续下去了,使用 break)。
使用 HashSet 而非 ArrayList 的原因是 ArrayList 的 contains 方法的效率不高。