Skip to content

Commit 0ea72d2

Browse files
committed
修改最长不含重复字符的子串解法
1 parent 7c5c9dc commit 0ea72d2

File tree

2 files changed

+61
-13
lines changed

2 files changed

+61
-13
lines changed

code/offer/src/Chap5/LongestSubstring.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,34 @@ public int findLongestSubstring(String str) {
1616

1717
for (int i = 0; i < str.length(); i++) {
1818
int preIndex = position[str.charAt(i) - 'a'];
19+
// 字符第一次出现,或者d > f(i -1)
1920
if (preIndex == -1 || i - preIndex > curLen) curLen++;
20-
else {
21-
if (curLen > maxLen) maxLen = curLen;
22-
curLen = i - preIndex;
23-
}
21+
// d <= f(i -1)
22+
else curLen = i - preIndex;
23+
// 记录当前字符出现的位置
2424
position[str.charAt(i) - 'a'] = i;
25+
if (curLen > maxLen) maxLen = curLen;
26+
}
27+
return maxLen;
28+
}
29+
30+
public int findLongestSubstring2(String str) {
31+
int curLen = 0;
32+
int maxLen = 0;
33+
int preIndex = -1;
34+
// 0~25表示a~z,position[0] = index,表明a上次出现在index处
35+
int[] position = new int[26];
36+
for (int i = 0; i < 26; i++) {
37+
position[i] = -1;
38+
}
39+
40+
for (int i = 0; i < str.length(); i++) {
41+
preIndex = Math.max(preIndex, position[str.charAt(i) - 'a']);
42+
curLen = i - preIndex;
43+
// 记录当前字符出现的位置
44+
position[str.charAt(i) - 'a'] = i;
45+
maxLen = Math.max(curLen, maxLen);
2546
}
26-
if (curLen > maxLen) maxLen = curLen;
2747
return maxLen;
2848
}
2949

notes/剑指offer面试题48--最长不含重复字符串的子字符串.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,53 @@ public class LongestSubstring {
3535
// 字符第一次出现,或者d > f(i -1)
3636
if (preIndex == -1 || i - preIndex > curLen) curLen++;
3737
// d <= f(i -1)
38-
else {
39-
if (curLen > maxLen) maxLen = curLen;
40-
curLen = i - preIndex;
41-
}
38+
else curLen = i - preIndex;
4239
// 记录当前字符出现的位置
4340
position[str.charAt(i) - 'a'] = i;
41+
if (curLen > maxLen) maxLen = curLen;
4442
}
45-
if (curLen > maxLen) maxLen = curLen;
4643
return maxLen;
4744
}
4845
}
4946
5047
```
5148
52-
用了一个数组position代替哈希表,记录'a'~'z'每个字符上次出现的位置,能以O(1)的时间完成,先要将position中的值全初始化为-1,因为上次出现的位置可能含有索引0。 curLen就是上面说到的$f(i -1)$
49+
用了一个数组position代替哈希表,记录'a'~'z'每个字符上次出现的位置,能以O(1)的时间完成,先要将position中的值全初始化为-1,因为上次出现的位置可能含有索引0。 curLen就是上面说到的$f(i -1)$
5350

54-
如果某个字符第一次出现,那么它上次出现的位置preIndex为-1。当前最长不重复子字符串直接加1
51+
类似的解法
5552

56-
只有else语句中curLen才可能变小,因此要即使保存到maxLen,因为这个curLen可能就是最长的。而且if语句中curLen++并没有和maxLen比较,所以除了循环后还要再和maxLen比较一次。
53+
用一个全局preIndex表示重复字符的最远位置,或者说上一次出现重复字符的位置。
54+
55+
还是上面的例子。
56+
57+
-`d <= f(i-1)`,如bdcefgc,在当前c未出现时,preIndex是-1,因为还没有出现过重复字符,当前c出现后,有重复字符了,preIndex是索引2处的c。i - preIndex == 4就表示了以当前c结尾的最长不重复子串长度。
58+
-`d > f(i-1)`,如erabcdabr,当前r未出现时,上一个重复字符是b,其preIndex是3。当前r出现后,r上一次出现的位置是1,比较两者,显然b的上次出现位置靠右些,所以应该以b的preIndex为准(如果以r的preIndex为准,会将重复的b包含进去)
59+
60+
综和以上两种情况,需要计算1)不计入当前字符时最近一个重复字符的上次出现位置,2)算上当前字符时,当前字符的上次出现位置。取这两者的最大值,即取两个位置中更靠后的位置。`preIndex = Math.max(preIndex, position[ch - 'a'])`,然后`i - preIndex`表示以位置i处字符结尾的最长不重复子串的长度。
61+
62+
```java
63+
public class LongestSubstring {
64+
public int findLongestSubstring(String str) {
65+
int curLen = 0;
66+
int maxLen = 0;
67+
int preIndex = -1;
68+
// 0~25表示a~z,position[0] = index,表明a上次出现在index处
69+
int[] position = new int[26];
70+
for (int i = 0; i < 26; i++) {
71+
position[i] = -1;
72+
}
73+
74+
for (int i = 0; i < str.length(); i++) {
75+
preIndex = Math.max(preIndex, position[str.charAt(i) - 'a']);
76+
curLen = i - preIndex;
77+
// 记录当前字符出现的位置
78+
position[str.charAt(i) - 'a'] = i;
79+
maxLen = Math.max(curLen, maxLen);
80+
}
81+
return maxLen;
82+
}
83+
}
84+
```
5785

5886
---
5987

0 commit comments

Comments
 (0)