Skip to content

Commit b3e7f5d

Browse files
authored
feat: add solutions to lcof2 problem: No.016 (doocs#1470)
No.016.不含重复字符的最长子字符串
1 parent c634615 commit b3e7f5d

File tree

6 files changed

+127
-118
lines changed

6 files changed

+127
-118
lines changed

lcof2/剑指 Offer II 016. 不含重复字符的最长子字符串/README.md

Lines changed: 68 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@
5757

5858
<!-- 这里可写通用的实现逻辑 -->
5959

60-
因为 `s` 中可能会出现字母、数字、符号和空格,所以可以用哈希表表示窗口
60+
**方法一:双指针 + 哈希表**
61+
62+
我们用两个指针 $j$ 和 $i$ 维护一个不包含重复字符的子串,其中 $j$ 为子串的左边界,$i$ 为子串的右边界,用一个哈希表或数组 $ss$ 记录窗口中所有出现过的字符。
63+
64+
接下来,我们遍历字符串 $s$,对于当前遍历到的字符 $s[i]$,如果 $s[i]$ 在 $[j, i)$ 范围内有与 $s[i]$ 相同的字符,我们就不断地向右移动指针 $j$,直到 $ss[s[i]]$ 为 `false`,此时 $[j,i)$ 中没有任何与 $s[i]$ 相同的字符,我们就找到了以字符 $s[i]$ 为结尾的最长子串。对于每个 $i$,我们都更新最长子串的长度,最终返回答案。
65+
66+
时间复杂度 $O(n)$,空间复杂度 $O(|\Sigma|)$,其中 $n$ 为字符串 $s$ 的长度,而 $\Sigma$ 表示字符集,本题中字符集为所有 ASCII 码在 $[0, 128)$ 内的字符,即 $|\Sigma|=128$。
6167

6268
<!-- tabs:start -->
6369

@@ -68,17 +74,14 @@
6874
```python
6975
class Solution:
7076
def lengthOfLongestSubstring(self, s: str) -> int:
71-
window = defaultdict(int)
72-
n, ans = len(s), 0
73-
left, right = 0, 0
74-
while right < n:
75-
ch = s[right]
76-
right += 1
77-
window[ch] += 1
78-
while window[ch] > 1:
79-
window[s[left]] -= 1
80-
left += 1
81-
ans = max(ans, right - left)
77+
ss = set()
78+
ans = j = 0
79+
for i, c in enumerate(s):
80+
while c in ss:
81+
ss.remove(s[j])
82+
j += 1
83+
ans = max(ans, i - j + 1)
84+
ss.add(c)
8285
return ans
8386
```
8487

@@ -89,41 +92,58 @@ class Solution:
8992
```java
9093
class Solution {
9194
public int lengthOfLongestSubstring(String s) {
92-
Map<Character, Integer> window = new HashMap<>();
93-
int n = s.length(), ans = 0;
94-
int left = 0, right = 0;
95-
while (right < n) {
96-
char ch = s.charAt(right++);
97-
window.merge(ch, 1, Integer::sum);
98-
while (window.get(ch) > 1) {
99-
window.merge(s.charAt(left++), -1, Integer::sum);
95+
boolean[] ss = new boolean[128];
96+
int ans = 0, j = 0;
97+
int n = s.length();
98+
for (int i = 0; i < n; ++i) {
99+
char c = s.charAt(i);
100+
while (ss[c]) {
101+
ss[s.charAt(j++)] = false;
100102
}
101-
ans = Math.max(ans, right - left);
103+
ans = Math.max(ans, i - j + 1);
104+
ss[c] = true;
102105
}
103106
return ans;
104107
}
105108
}
106109
```
107110

111+
### **C++**
112+
113+
```cpp
114+
class Solution {
115+
public:
116+
int lengthOfLongestSubstring(string s) {
117+
bool ss[128] = {false};
118+
int n = s.size();
119+
int ans = 0;
120+
for (int i = 0, j = 0; i < n; ++i) {
121+
while (ss[s[i]]) {
122+
ss[s[j++]] = false;
123+
}
124+
ss[s[i]] = true;
125+
ans = max(ans, i - j + 1);
126+
}
127+
return ans;
128+
}
129+
};
130+
```
131+
108132
### **Go**
109133
110134
```go
111-
func lengthOfLongestSubstring(s string) int {
112-
window := make(map[byte]int)
113-
n := len(s)
114-
ans := 0
115-
left, right := 0, 0
116-
for right < n {
117-
ch := s[right]
118-
right++
119-
window[ch]++
120-
for window[ch] > 1 {
121-
window[s[left]]--
122-
left++
135+
func lengthOfLongestSubstring(s string) (ans int) {
136+
ss := make([]bool, 128)
137+
j := 0
138+
for i, c := range s {
139+
for ss[c] {
140+
ss[s[j]] = false
141+
j++
123142
}
124-
ans = max(ans, right-left)
143+
ss[c] = true
144+
ans = max(ans, i-j+1)
125145
}
126-
return ans
146+
return
127147
}
128148
129149
func max(a, b int) int {
@@ -134,32 +154,22 @@ func max(a, b int) int {
134154
}
135155
```
136156

137-
### **C++**
138-
139-
```cpp
140-
class Solution {
141-
public:
142-
int lengthOfLongestSubstring(string s) {
143-
if (s.size() == 0)
144-
return 0;
145-
146-
int left = 0;
147-
int maxlen = 0;
148-
unordered_set<char> hash;
149-
150-
for (int right = 0; right < s.size(); right++) {
151-
while (hash.find(s[right]) != hash.end()) {
152-
hash.erase(s[left]);
153-
left++;
154-
}
157+
### **TypeScript**
155158

156-
hash.insert(s[right]);
157-
maxlen = max(maxlen, right - left + 1);
159+
```ts
160+
function lengthOfLongestSubstring(s: string): number {
161+
let ans = 0;
162+
const n = s.length;
163+
const ss: boolean[] = new Array(128).fill(false);
164+
for (let i = 0, j = 0; i < n; ++i) {
165+
while (ss[s.charCodeAt(i)]) {
166+
ss[s.charCodeAt(j++)] = false;
158167
}
159-
160-
return maxlen;
168+
ss[s.charCodeAt(i)] = true;
169+
ans = Math.max(ans, i - j + 1);
161170
}
162-
};
171+
return ans;
172+
}
163173
```
164174

165175
### **...**
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
class Solution {
22
public:
33
int lengthOfLongestSubstring(string s) {
4-
if (s.size() == 0)
5-
return 0;
6-
7-
int left = 0;
8-
int maxlen = 0;
9-
unordered_set<char> hash;
10-
11-
for (int right = 0; right < s.size(); right++) {
12-
while (hash.find(s[right]) != hash.end()) {
13-
hash.erase(s[left]);
14-
left++;
4+
bool ss[128] = {false};
5+
int n = s.size();
6+
int ans = 0;
7+
for (int i = 0, j = 0; i < n; ++i) {
8+
while (ss[s[i]]) {
9+
ss[s[j++]] = false;
1510
}
16-
17-
hash.insert(s[right]);
18-
maxlen = max(maxlen, right - left + 1);
11+
ss[s[i]] = true;
12+
ans = max(ans, i - j + 1);
1913
}
20-
21-
return maxlen;
14+
return ans;
2215
}
2316
};

lcof2/剑指 Offer II 016. 不含重复字符的最长子字符串/Solution.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
func lengthOfLongestSubstring(s string) int {
2-
window := make(map[byte]int)
3-
n := len(s)
4-
ans := 0
5-
left, right := 0, 0
6-
for right < n {
7-
ch := s[right]
8-
right++
9-
window[ch]++
10-
for window[ch] > 1 {
11-
window[s[left]]--
12-
left++
1+
func lengthOfLongestSubstring(s string) (ans int) {
2+
ss := make([]bool, 128)
3+
j := 0
4+
for i, c := range s {
5+
for ss[c] {
6+
ss[s[j]] = false
7+
j++
138
}
14-
ans = max(ans, right-left)
9+
ss[c] = true
10+
ans = max(ans, i-j+1)
1511
}
16-
return ans
12+
return
1713
}
1814

1915
func max(a, b int) int {
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
class Solution {
2-
public int lengthOfLongestSubstring(String s) {
3-
Map<Character, Integer> window = new HashMap<>();
4-
int n = s.length(), ans = 0;
5-
int left = 0, right = 0;
6-
while (right < n) {
7-
char ch = s.charAt(right++);
8-
window.merge(ch, 1, Integer::sum);
9-
while (window.get(ch) > 1) {
10-
window.merge(s.charAt(left++), -1, Integer::sum);
11-
}
12-
ans = Math.max(ans, right - left);
13-
}
14-
return ans;
15-
}
16-
}
1+
class Solution {
2+
public int lengthOfLongestSubstring(String s) {
3+
boolean[] ss = new boolean[128];
4+
int ans = 0, j = 0;
5+
int n = s.length();
6+
for (int i = 0; i < n; ++i) {
7+
char c = s.charAt(i);
8+
while (ss[c]) {
9+
ss[s.charAt(j++)] = false;
10+
}
11+
ans = Math.max(ans, i - j + 1);
12+
ss[c] = true;
13+
}
14+
return ans;
15+
}
16+
}
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
class Solution:
2-
def lengthOfLongestSubstring(self, s: str) -> int:
3-
window = defaultdict(int)
4-
n, ans = len(s), 0
5-
left, right = 0, 0
6-
while right < n:
7-
ch = s[right]
8-
right += 1
9-
window[ch] += 1
10-
while window[ch] > 1:
11-
window[s[left]] -= 1
12-
left += 1
13-
ans = max(ans, right - left)
14-
return ans
1+
class Solution:
2+
def lengthOfLongestSubstring(self, s: str) -> int:
3+
ss = set()
4+
ans = j = 0
5+
for i, c in enumerate(s):
6+
while c in ss:
7+
ss.remove(s[j])
8+
j += 1
9+
ans = max(ans, i - j + 1)
10+
ss.add(c)
11+
return ans
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function lengthOfLongestSubstring(s: string): number {
2+
let ans = 0;
3+
const n = s.length;
4+
const ss: boolean[] = new Array(128).fill(false);
5+
for (let i = 0, j = 0; i < n; ++i) {
6+
while (ss[s.charCodeAt(i)]) {
7+
ss[s.charCodeAt(j++)] = false;
8+
}
9+
ss[s.charCodeAt(i)] = true;
10+
ans = Math.max(ans, i - j + 1);
11+
}
12+
return ans;
13+
}

0 commit comments

Comments
 (0)