剑指 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。
通过以上的分析后,就可以编写代码了。
代码
|
|