题目描述:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
最简单的就是遍历,循环遍历每个子串。
class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLen = 0;
int curLen = 0;
char[] arr = s.toCharArray();
for(int i = 0; i < arr.length; ++i)
{
curLen = 0;
List<Character> list = new ArrayList<Character>();
for(int j = i; j < arr.length; ++j)
{
if(!list.contains(arr[j])) // 如果还未出现重复字符,计数器自增
{
list.add(arr[j]);
++curLen;
}
else
break;
}
if(curLen > maxLen)
{
maxLen = curLen;
}
}
return maxLen;
}
}
遍历每个子串的复杂度太高,不考虑使用List检测重复字符的问题,需要两层循环,时间复杂度为O(n*n).
可以考虑使用滑动窗口解决。使用一个指针标记子串的起始位置,记为start,使用一个指针标记子串的结束位置,记为end。不断向后移动end指针,当遇到第一个和[start, end]之间重复的字符时,计算该字符在[start,end]中的位置,记为index,则[index+1, end]之间不存在重复字符,将start移动到index+1的位置,此时[start,end]之间不再有重复字符,继续移动end指针。为了快速获取字符在之前出现的位置,可以使用HashMap做一下缓存,以空间换时间。具体代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLen = 0;
int curLen = 0;
char[] arr = s.toCharArray();
Map<Character, Integer> indexCache = new HashMap<Character, Integer>(); // 缓存之前字符出现的位置
int start = 0;
int end = 0;
for(end = 0; end < arr.length; ++end)
{
int index = indexCache.getOrDefault(arr[end], -1); // 当前字符在之前子串中的位置
if(index >= 0 && index >= start) // 之前已经存在该字符,并且字符在[start, end],说明当前子串有重复字符
{
start = index + 1;
}
indexCache.put(arr[end], end);
curLen = end - start + 1; // 更新当前子串的长度
if(curLen > maxLen)
{
maxLen = curLen;
}
}
return maxLen;
}
}
该方法只做了一次遍历,时间复杂度为O(n),由于使用了HashMap做位置的缓存,空间复杂度也为O(n).