题目描述:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
方法一:暴力法
思路:检查所有的子字符串,看他是否不含有重复的字符
public static int lengthOfLongestSubstring(String s) {
int len=0;
int i, j, k;
for (i=0;i<s.length();i++){
for (j=i+1;j<=s.length();j++){
if (allUnique(s,i,j)){
len = Math.max(len,j-i);
}
}
}
return len;
}
public static boolean allUnique(String s, int start, int end) {
/**Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。没有索引,没有带索引的方法,也不能使用普通的for循环遍历。
* 两个常用的实现类:HashSet、LinkedHashSet
* HashSet:
* java.util.HashSet底层的实现其实是一个java.util.HashMap支持
* HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode与equals方法。
* HashSet特点:
不允许存储重复的元素
没有索引,没有带索引的方法,也不能使用普通的for循环遍历
是一个无序的集合,存储元素和取出元素的顺序有可能不一致
底层是一个哈希表结构(查询的速度非常的快)
*/
int i;
Set<Character> set = new HashSet<>();
for (i=start;i<end;i++){
Character ch = s.charAt(i);
if (set.contains(ch)){
return false;
}
set.add(ch);
}
return true;
}
方法二:滑动窗口
在暴力法中,会反复检查一个子字符串是否含有重复的字符,但如果从索引 i i i到 j − 1 j-1 j−1之间的子字符串 s i j s_{ij} sij已经被检查为没有重复字符。那么只需要检查 s [ j ] s[j] s[j]对应的字符是否已经存在与子字符串 s i j s_{ij} sij中。
滑动窗口: 滑动窗口是数组/字符串中常用的抽象概念。窗口通常是数组/字符串中由开始和结束索引定义的一系列元素的集合,即
[
i
,
j
)
[i,j)
[i,j)(左闭右开)。而滑动窗口是可以将两个边界向某一方向“滑动“的窗口。
简单的说就是遍历的时候,两个指针一前一后夹着子串(子数组),类似于窗口,这个窗口的大小和范围会随着前后指针的移动发生变化。(双指针确定一个窗口)。
本题的思路就是: 保证右指针每次往前移动一格,每次移动都会有一个新的元素进入窗口。这时就来判断原窗口中是否包含这个元素,如果不包含,则左指针不动,包含,则找到该元素对应的位置,将左指针移到该位置,重新计算无重复字符的最长子串长度。
既然涉及到“找到某元素对应的位置”,这里就考虑使用hashMap,键值对,该类有一个方法:
get(Object key)
返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null。
所以可以在key域存放字母,value域存放对应的下标,来实现以上操作
具体代码如下:
private static int lengthOfLongestSubstring(String s) {
int len = 0, i = 0, j = 0;
HashMap<Character,Integer> hashMap = new HashMap<>();
while(j<s.length()){
Character ch = s.charAt(j);
if (hashMap.containsKey(ch)){
i = Math.max(i,hashMap.get(ch));
}
hashMap.put(ch,++j);
len = Math.max(len,j-i);
}
return len;
}