39
39
40
40
## 解法
41
41
42
- ### 方法一:双指针 + 计数器
42
+ ### 方法一:滑动窗口 + 哈希表
43
43
44
- 我们观察发现,字符均为小写字母,也即最多有 $26$ 种不同的字符。因此,如果 $k \gt 26$ 或者 $k \gt n$,则无法找到任何长度为 $k$ 且不含重复字符的子串,直接返回 $0$ 即可 。
44
+ 我们维护一个长度为 $k$ 的滑动窗口,用一个哈希表 $cnt$ 统计窗口中每个字符的出现次数 。
45
45
46
- 接下来,我们用双指针 $j$ 和 $i$ 维护一个滑动窗口,其中 $j$ 是滑动窗口的左端点,$i$ 是滑动窗口的右端点,用一个计数器 $cnt$ 统计滑动窗口中每个字符出现的次数 。
46
+ 首先,我们将字符串 $s$ 的前 $k$ 个字符加入哈希表 $cnt$ 中,并判断 $cnt$ 的大小是否等于 $k$,如果等于 $k$,则说明窗口中的字符都不相同,答案 $ans$ 加一 。
47
47
48
- 遍历字符串 $ s$,每次将 $s[ i] $ 加入滑动窗口,即 $cnt[ s[ i]] ++$,如果此时 $cnt[ s[ i]] \gt 1$ 或者 $i - j + 1 \gt k$,则循环将 $s[ j ] $ 从滑动窗口中移除,即 $cnt[ s [ j ]] --$,并将 $j$ 右移。如果 $j$ 右移结束后,窗口大小 $i - j + 1$ 恰好等于 $ k$,则说明滑动窗口中的字符串是一个符合题意的子串,将结果加一 。
48
+ 接下来,我们从 $k$ 开始遍历字符串 $ s$,每次将 $s[ i] $ 加入哈希表 $cnt$ 中,同时将 $ s[ i-k ] $ 从哈希表 $cnt$ 中减一,如果 $cnt[ s[ i-k ]] $ 减一后等于 $0$,则将 $s[ i-k ] $ 从哈希表 $cnt$ 中删除。如果此时哈希表 $cnt$ 的大小等于 $ k$,则说明窗口中的字符都不相同,答案 $ans$ 加一 。
49
49
50
- 遍历结束后,即可得到所有符合题意的子串的个数 。
50
+ 最后,返回答案 $ans$ 即可 。
51
51
52
- 时间复杂度 $O(n)$,空间复杂度 $O(C)$。 其中 $n$ 为字符串 $s$ 的长度;而 $C$ 为字符集的大小,本题中 $C = 26$。
52
+ 时间复杂度 $O(n)$,空间复杂度 $O(\min(k, |\Sigma|))$, 其中 $n$ 为字符串 $s$ 的长度;而 $\Sigma$ 为字符集,本题中字符集为小写英文字母,所以 $|\Sigma| = 26$。
53
53
54
54
<!-- tabs:start -->
55
55
56
56
``` python
57
57
class Solution :
58
58
def numKLenSubstrNoRepeats (self , s : str , k : int ) -> int :
59
- n = len (s)
60
- if k > n or k > 26 :
61
- return 0
62
- ans = j = 0
63
- cnt = Counter()
64
- for i, c in enumerate (s):
65
- cnt[c] += 1
66
- while cnt[c] > 1 or i - j + 1 > k:
67
- cnt[s[j]] -= 1
68
- j += 1
69
- ans += i - j + 1 == k
59
+ cnt = Counter(s[:k])
60
+ ans = int (len (cnt) == k)
61
+ for i in range (k, len (s)):
62
+ cnt[s[i]] += 1
63
+ cnt[s[i - k]] -= 1
64
+ if cnt[s[i - k]] == 0 :
65
+ cnt.pop(s[i - k])
66
+ ans += int (len (cnt) == k)
70
67
return ans
71
68
```
72
69
73
70
``` java
74
71
class Solution {
75
72
public int numKLenSubstrNoRepeats (String s , int k ) {
76
73
int n = s. length();
77
- if (k > n || k > 26 ) {
74
+ if (n < k ) {
78
75
return 0 ;
79
76
}
80
- int [] cnt = new int [128 ];
81
- int ans = 0 ;
82
- for (int i = 0 , j = 0 ; i < n; ++ i) {
83
- ++ cnt[s. charAt(i)];
84
- while (cnt[s. charAt(i)] > 1 || i - j + 1 > k) {
85
- cnt[s. charAt(j++ )]-- ;
77
+ Map<Character , Integer > cnt = new HashMap<> (k);
78
+ for (int i = 0 ; i < k; ++ i) {
79
+ cnt. merge(s. charAt(i), 1 , Integer :: sum);
80
+ }
81
+ int ans = cnt. size() == k ? 1 : 0 ;
82
+ for (int i = k; i < n; ++ i) {
83
+ cnt. merge(s. charAt(i), 1 , Integer :: sum);
84
+ if (cnt. merge(s. charAt(i - k), - 1 , Integer :: sum) == 0 ) {
85
+ cnt. remove(s. charAt(i - k));
86
86
}
87
- ans += i - j + 1 == k ? 1 : 0 ;
87
+ ans += cnt . size() == k ? 1 : 0 ;
88
88
}
89
89
return ans;
90
90
}
@@ -96,17 +96,20 @@ class Solution {
96
96
public:
97
97
int numKLenSubstrNoRepeats(string s, int k) {
98
98
int n = s.size();
99
- if (k > n || k > 26 ) {
99
+ if (n < k ) {
100
100
return 0;
101
101
}
102
- int cnt[ 128] {};
103
- int ans = 0;
104
- for (int i = 0, j = 0; i < n; ++i) {
102
+ unordered_map<char, int> cnt;
103
+ for (int i = 0; i < k; ++i) {
104
+ ++cnt[ s[ i]] ;
105
+ }
106
+ int ans = cnt.size() == k;
107
+ for (int i = k; i < n; ++i) {
105
108
++cnt[ s[ i]] ;
106
- while ( cnt[ s[ i]] > 1 || i - j + 1 > k ) {
107
- -- cnt[ s [ j++ ]] ;
109
+ if (-- cnt[ s[ i - k ]] == 0 ) {
110
+ cnt.erase(s [ i - k ] ) ;
108
111
}
109
- ans += i - j + 1 == k;
112
+ ans += cnt.size() == k;
110
113
}
111
114
return ans;
112
115
}
@@ -115,17 +118,24 @@ public:
115
118
116
119
```go
117
120
func numKLenSubstrNoRepeats(s string, k int) (ans int) {
118
- if k > len(s) || k > 26 {
119
- return 0
121
+ n := len(s)
122
+ if n < k {
123
+ return
124
+ }
125
+ cnt := map[byte]int{}
126
+ for i := 0; i < k; i++ {
127
+ cnt[s[i]]++
128
+ }
129
+ if len(cnt) == k {
130
+ ans++
120
131
}
121
- cnt := [128]int{}
122
- for i, j := 0, 0; i < len(s); i++ {
132
+ for i := k; i < n; i++ {
123
133
cnt[s[i]]++
124
- for cnt[s[i]] > 1 || i-j+1 > k {
125
- cnt[s[j]]--
126
- j++
134
+ cnt[s[i-k]]--
135
+ if cnt[s[i-k]] == 0 {
136
+ delete(cnt, s[i-k])
127
137
}
128
- if i-j+1 == k {
138
+ if len(cnt) == k {
129
139
ans++
130
140
}
131
141
}
@@ -136,7 +146,7 @@ func numKLenSubstrNoRepeats(s string, k int) (ans int) {
136
146
``` ts
137
147
function numKLenSubstrNoRepeats(s : string , k : number ): number {
138
148
const n = s .length ;
139
- if (k > n ) {
149
+ if (n < k ) {
140
150
return 0 ;
141
151
}
142
152
const cnt: Map <string , number > = new Map ();
@@ -164,120 +174,34 @@ class Solution {
164
174
* @return Integer
165
175
*/
166
176
function numKLenSubstrNoRepeats($s, $k) {
167
- $sum = ($k * ($k + 1)) / 2 - $k;
168
- $cnt = $tmp = 0;
169
- for ($i = 0; $i < strlen($s) - $k + 1; $i++) {
170
- $str = substr($s, $i, $k);
171
- for ($j = 0; $j < $k; $j++) {
172
- $tmp += strpos($str, $str[$j]);
173
- }
174
- if ($tmp === $sum) {
175
- $cnt++;
176
- }
177
- $tmp = 0;
178
- }
179
- return $cnt;
180
- }
181
- }
182
- ```
183
-
184
- <!-- tabs: end -->
185
-
186
- ### 方法二
187
-
188
- <!-- tabs: start -->
189
-
190
- ``` python
191
- class Solution :
192
- def numKLenSubstrNoRepeats (self , s : str , k : int ) -> int :
193
- n = len (s)
194
- if k > n:
195
- return 0
196
- cnt = Counter(s[:k])
197
- ans = int (len (cnt) == k)
198
- for i in range (k, n):
199
- cnt[s[i]] += 1
200
- cnt[s[i - k]] -= 1
201
- if cnt[s[i - k]] == 0 :
202
- cnt.pop(s[i - k])
203
- ans += len (cnt) == k
204
- return ans
205
- ```
206
-
207
- ``` java
208
- class Solution {
209
- public int numKLenSubstrNoRepeats (String s , int k ) {
210
- int n = s. length();
211
- if (k > n) {
177
+ $n = strlen($s);
178
+ if ($n < $k) {
212
179
return 0;
213
180
}
214
- Map<Character , Integer > cnt = new HashMap<> (k);
215
- for (int i = 0 ; i < k; ++ i) {
216
- cnt. merge(s. charAt(i), 1 , Integer :: sum);
217
- }
218
- int ans = cnt. size() == k ? 1 : 0 ;
219
- for (int i = k; i < n; ++ i) {
220
- cnt. merge(s. charAt(i), 1 , Integer :: sum);
221
- if (cnt. merge(s. charAt(i - k), - 1 , Integer :: sum) == 0 ) {
222
- cnt. remove(s. charAt(i - k));
181
+ $cnt = [];
182
+ for ($i = 0; $i < $k; ++$i) {
183
+ if (!isset($cnt[$s[$i]])) {
184
+ $cnt[$s[$i]] = 1;
185
+ } else {
186
+ $cnt[$s[$i]]++;
223
187
}
224
- ans += cnt. size() == k ? 1 : 0 ;
225
- }
226
- return ans;
227
- }
228
- }
229
- ```
230
-
231
- ``` cpp
232
- class Solution {
233
- public:
234
- int numKLenSubstrNoRepeats(string s, int k) {
235
- int n = s.size();
236
- if (k > n) {
237
- return 0;
238
188
}
239
- unordered_map<char, int> cnt;
240
- for (int i = 0; i < k; ++i) {
241
- cnt[ s[ i]] ++;
242
- }
243
- int ans = cnt.size() == k ? 1 : 0;
244
- for (int i = k; i < n; ++i) {
245
- cnt[ s[ i]] ++;
246
- cnt[ s[ i - k]] --;
247
- if (cnt[ s[ i - k]] == 0) {
248
- cnt.erase(s[ i - k] );
189
+ $ans = count($cnt) == $k ? 1 : 0;
190
+ for ($i = $k; $i < $n; ++$i) {
191
+ if (!isset($cnt[$s[$i]])) {
192
+ $cnt[$s[$i]] = 1;
193
+ } else {
194
+ $cnt[$s[$i]]++;
249
195
}
250
- ans += cnt.size() == k ? 1 : 0;
196
+ if ($cnt[$s[$i - $k]] - 1 == 0) {
197
+ unset($cnt[$s[$i - $k]]);
198
+ } else {
199
+ $cnt[$s[$i - $k]]--;
200
+ }
201
+ $ans += count($cnt) == $k ? 1 : 0;
251
202
}
252
- return ans;
203
+ return $ ans;
253
204
}
254
- };
255
- ```
256
-
257
- ```go
258
- func numKLenSubstrNoRepeats(s string, k int) (ans int) {
259
- n := len(s)
260
- if k > n {
261
- return 0
262
- }
263
- cnt := map[byte]int{}
264
- for i := 0; i < k; i++ {
265
- cnt[s[i]]++
266
- }
267
- if len(cnt) == k {
268
- ans++
269
- }
270
- for i := k; i < n; i++ {
271
- cnt[s[i]]++
272
- cnt[s[i-k]]--
273
- if cnt[s[i-k]] == 0 {
274
- delete(cnt, s[i-k])
275
- }
276
- if len(cnt) == k {
277
- ans++
278
- }
279
- }
280
- return
281
205
}
282
206
```
283
207
0 commit comments