力扣3-无重复字符的最长子串(中等)

题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的最长子串 的长度。

方法一:滑动窗口(官方)

分析:

  • (a)bcabcbb 开始的最长字符串为(abc)abcbb
  • a(b)cabcbb开始的最长字符串为a(bca)bcbb
  • ab(c)abcbb开始的最长字符串为ab(cab)cbb
  • ……

需要考虑的特殊情况有:

  • 字符串为空的情况
  • 字符串均为重复字符的情况
  • 测试其他常规输入

算法:

  • 我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的Rk

  • 在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

  • 在枚举结束后,我们找到的最长的子串的长度即为答案。

判断重复字符

在上面的流程中,还需要使用一种数据结构来判断是否有重复的字符,常用的数据结构是哈希集合。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,往哈希集合中添加一个字符

①官方代码(使用哈希保存字符)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> occ = new HashSet<Character>();
int n = s.length();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.remove(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}

②我的代码(使用int数组保存状态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Question3 {
public static void main(String[] args) {
Question3 question3 = new Question3();
int maxLength = question3.lengthOfLongestSubstring("dvdf");
System.out.println(maxLength);
}

public int lengthOfLongestSubstring(String s) {
if (s.equals("")) return 0;

int[] length = new int[s.length()];
for (int i = 0; i < length.length; i++) {
length[i] = 1;
}
int maxLength = length[0];

char[] charArray = s.toCharArray();
for (int i = 1; i < charArray.length; i++) {
int diffStringLength = getLastCommonCharBefore(s, charArray[i], i - length[i - 1], i);
if (diffStringLength == length[i - 1]) { // 当前字符可以直接加入前一个字符所在子串
length[i] += length[i - 1];
maxLength = maxLength > length[i] ? maxLength : length[i];
} else { // 当前字符只能加入前一个字符所在子串的子串中去(即有字符和当前字符一样)
length[i] = diffStringLength + 1;
maxLength = maxLength > length[i] ? maxLength : length[i];
}
}
return maxLength;
}
/**
* @param string 源字符串
* @param targetChar 查找的字符
* @param startIndex 源字符串要查找字符范围的起点(包括)
* @param endIndex 源字符串要查找字符范围的终点(不包括)
* @return 前一个字符所在的子串中可以与当前字符处在同一个子串中的子串的最大长度
*/
public int getLastCommonCharBefore(String string, char targetChar, int startIndex, int endIndex) {
String substring = string.substring(startIndex, endIndex);// beginIndex:指定子串的起始索引位置(包含),endIndex:指定子串的结束索引位置(不包含)
int index = substring.indexOf(targetChar);
return substring.length() - 1 - index;
}
}

方法二:暴力求解

思路:遍历所有子串进行判断

评价:一般不用

Contents
  1. 1. 题目描述
    1. 1.0.1. 方法一:滑动窗口(官方)
    2. 1.0.2. 方法二:暴力求解
|