剑指 Offer 48. 最长不含重复字符的子字符串

题目描述

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

输入输出

1
2
3
4
5
6
7
8
9
10
11
12
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

基本思路

  1. 双指针+哈希表:
    1. 哈希表统计s[j]最后一次出现的索引
    2. 更新左指针
    3. res记录res和j-i之间的较大值
  2. 动态规划 -> dp[j]代表以字符s[j]为结尾的”最长不重复子字符串”的长度
    1. i < 0 即s[j]左边无相同字符 则dp[j] = dp[j - 1] + 1
    2. dp[j - 1] < j - i 说明s[i]在子字符串dp[j - 1]区间之外 则dp[j] = dp[j - 1] + 1
    3. dp[j - 1] >= j - i 说明s[i]在子字符串dp[j - 1]区间之中 则dp[j]左边界有s[i]决定 即dp[j] = j - i
    4. 而因为i < 0时dp[j - 1] <= j恒成立 因而dp[j - 1] < j - i恒成立 因此2.1分支和2.2分支可合并 合并后:
    5. 最后求 max(dp[j - 1], dp[j]) 得到结果

$$
dp[j]=
\begin{cases}
dp[j-1]+1 & , dp[j-1]<j-i \\[3ex]
j-i & , dp[j-1] \geq j-i
\end{cases}
$$

java实现

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
// 双指针+哈希表
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> dic = new HashMap<>();
int i = -1, res = 0;
for(int j = 0; j < s.length(); j++) {
if(dic.containsKey(s.charAt(j)))
i = Math.max(i, dic.get(s.charAt(j))); // 更新左指针 i
dic.put(s.charAt(j), j); // 哈希表记录
res = Math.max(res, j - i); // 更新结果
}
return res;
}
}

// 动态规划
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> dic = new HashMap<>();
int res = 0, tmp = 0;
for(int j = 0; j < s.length(); j++){
int i = dic.getOrDefault(s.charAt(j), -1);
dic.put(s.charAt(j), j);
tmp = tmp < j-i ? tmp + 1 : j-i;
res = Math.max(res, tmp);
}
return res;
}
}