给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
今天刷题碰到这个题目,把这个题目的解题思路跟大家分享一下
分析:看到这个题目时,首先会想到去遍历这个字符串
- 当遍历一个新的字符时需要知道是否已经出现过
- 若出现过就得计算当前遍历的子字符的长度,记录下这个长度。
- 若没有出现过,则继续遍历,重复 1 2 3知道遍历结束。
这里有两个重要的难点:当出现重复字符时如何计算字符长度?如何知道遍历的新字符是否出现过,并且出现的位置在哪里?
解决以上两个问题:
- 我们定义一个变量startIndex记录子串的起始位置,用当前的遍历的下标(i)减去字符串开始下标(startIndex)就是子串的长度
- 我们需要定义一个义ASCII码值为下标的一个数组,初始化为全-1,代表该字符未出现过
int[] ascii = new int[128];
ascii[0] = -1;…ascii[127]=-1;
遍历字符时,根据字符拿到数组对应的值如果为-1,说明该字符未出现,若不是-1,说明可能出现过,这里为什么说可能出现过呢?因为当我们碰到以当前字符为下标的ASCII数组的值(也就是字符的下标)出现在startIndex的左边,是上一个子字符串出现的对于当前的字符串是无效的。当大于startIndex我们得计算出子串的值,用当前的遍历的下标(i)减去字符串开始下标(startIndex)
看下面一个例子:字符串“gfababfmpo”
g f a b a b f
startIndex: 0 0 0 0 0 3 5
ASCII码: 0 1 2 3 重复 重复
子符串长度: 4
最长度子字符串: 4
当遍历第2个a时,a的下标不是-1,说明重复,计算子字符串长度4-startIndex = 4-0 = 4,更新startIndex = 2+1 = 3,按对应下标更新为4
接下来遍历第2个b,b下标的不是-1,说明重复,计算子字符串长度5-3 =2,不更新最长子字符串。更新startIndex为b下标对应值+1=3+1=4 ,更新b下标的值为5
接下来遍历第二个f,f 下标值不是-1,但是不是已经出现,因为出现startIndex的左边,虽然已经出现过了,但是对于当前的子字符串没有关系,因此当不是-1还要判断是否大于等于startIndex,当两个条件都满足,说明字符重复出现。下面给出代码
public class Solution {
public static void main(String[] args) {
System.out.println(new Solution().lengthOfLongestSubstring("abcabcbb"));
}
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() <= 0) return 0;
if (s.length() == 1) return 1;
int[] ascii = new int[128];
//初始化数组为全-1
initArr(ascii);
int startIndex = 0;
int max = 0;
for (int i = 0; i < s.length(); i++) {
if (ascii[s.charAt(i)] != -1) {
if (ascii[s.charAt(i)] >= startIndex) {
//计算当前子串的长度
int len = (i - startIndex);
if (len > max) max = len;
//重新记录起始下标
startIndex = ascii[s.charAt(i)] + 1;
}
}
ascii[s.charAt(i)] = i;
}
if (max < (s.length() - startIndex)) max = s.length() - startIndex;
return max;
}
private void initArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = -1;
}
}
}