剑指 Offer 之 最长不含重复字符的子字符串
Contents
本文是对解题思路的整理。
题目描述
输入一个字符串(只包含 a ~ z 的字符),求其最长不含重复字符的子字符串的长度。
例如对于 arabcacfr
,最长不含重复字符的子字符串为 acfr
,长度为 4。
子串
和子序列
的区别?
子串
是字符串的一部分,该部分是由 连续 的字符构成的。例如字符串:abc
,子串有a
、b
、c
、ab
、bc
、abc
以及空串
,共有 $ {\frac{n(n+1)}{2}}+1 = 7 $ 种。
子序列
是也是由字符串中的字符构成的,即从最初序列通过去除某些元素但不破坏余下元素的相对位置
(在前或在后)而形成的新序列。例如字符串:abc
,子序列有a
、b
、c
、ab
、ac
、bc
、abc
以及空串
,共有 $ 2^n = 8 $ 种。
分析
这里使用 DP(Dynamic Programming) 的思想。
- 首先,让
f(i)
表示以第 i 个字符为结尾不重复子串的最长长度; - 然后,从左到右扫描字符串中的每个字符,当我们计算得到
f(i)
的时候,其实f(i + 1)
就已经知道了。因为f(i)
是依赖f(i + 1)
的; - 下面分两种情况:
- 如果第 i 个字符之前没有出现过,则
f(i) = f(i - 1) + 1
,加的这个 1 就表示把当前字符 i 加入到不重复子串中,其长度要增 1; - 如果第 i 个字符之前出现过,则计算当前第 i 个字符与它上次出现在字符串中的位置的距离
d
:- 如果
d <= f(i - 1)
,则f(i) = d
,(这种情况说明第 i 个字符上次在f(i - 1)
对应的最长子串中出现过); - 如果
d > f(i - 1)
,则f(i) = f(i - 1) + 1
,(这种情况说明第 i 个字符在f(i - 1)
对应的最长子串 之前 出现过)。
- 如果
- 如果第 i 个字符之前没有出现过,则
如何判断第 i 个字符之前有没有出现过?
- 可以使用 哈希表 或者 数组 实现。
例如字符串 arabcacfr
,如下所示:
字符 | a | r | a | b | c | a | c | f | r |
---|---|---|---|---|---|---|---|---|---|
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
- f(0) 对应的字符
a
之前没出现过,- 所以 f(0) = 1;
- 此时 f(0) 对应的子串为
a
。
- f(1) 对应的字符
r
之前没出现过,- 所以 f(1) = f(0) + 1 = 2;
- 此时 f(1) 对应的子串为
ar
。
- f(2) 对应的字符
a
之前出现过,此时需要判断距离d
。- 首先 d = 2 - 0 = 2;
- 然后,由于 d <= f(2 - 1) = f(1) = 2;
- 所以 f(2) = 2;
- 此时 f(2) 对应的子串为
ra
。
- f(3) 对应的字符
b
之前没出现过,- 所以 f(3) = f(2) + 1 = 3;
- 此时 f(3) 对应的子串为
rab
。
- f(4) 对应的字符
c
之前没出现过,- 所以 f(4) = f(3) + 1 = 4;
- 此时 f(4) 对应的子串为
rabc
。
- f(5) 对应的字符
a
之前出现过,此时需要判断距离d
。- 首先 d = 5 - 2 = 3;
- 然后,由于 d <= f(5 - 1) = f(4) = 4;
- 所以 f(5) = 3;
- 此时 f(5) 对应的子串为
bca
。
- f(6) 对应的字符
c
之前出现过,此时需要判断距离d
。- 首先 d = 6 - 4 = 2;
- 然后,由于 d <= f(6 - 1) = f(5) = 3;
- 所以 f(6) = 2;
- 此时 f(6) 对应的子串为
ac
。
- f(7) 对应的字符
f
之前没出现过,- 所以 f(7) = f(6) + 1 = 3;
- 此时 f(7) 对应的子串为
acf
。
- f(8) 对应的字符
r
之前出现过,此时需要判断距离d
。- 首先 d = 8 - 1 = 7;
- 然后,由于 d > f(8 - 1) = f(7) = 3;
- 所以 f(8) = f(8 - 1) + 1 = f(7) + 1 = 4;
- 此时 f(8) 对应的子串为
acfr
。
通过以上的分析后,就可以编写代码了。
代码
|
|