Skip to content

Commit a7a40da

Browse files
committed
Add solution 0395
1 parent b4eab90 commit a7a40da

27 files changed

+890
-525
lines changed

README.md

+372-368
Large diffs are not rendered by default.

ctl/models/tagproblem.go

+22-11
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,30 @@ func GenerateTagMdRows(solutionIds []int, metaMap map[int]TagList, mdrows []Mdro
130130
s5 := strings.Replace(s4, ")", "", -1)
131131
s6 := strings.Replace(s5, ",", "", -1)
132132
s7 := strings.Replace(s6, "?", "", -1)
133-
if internal {
134-
count := 0
135-
// 去掉 --- 这种情况,这种情况是由于题目标题中包含 - ,左右有空格,左右一填充,造成了 ---,3 个 -
136-
for i := 0; i < len(s7)-2; i++ {
137-
if s7[i] == '-' && s7[i+1] == '-' && s7[i+2] == '-' {
138-
fmt.Printf("【需要修正的标题是 %v】\n", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
139-
s7 = s7[:i+1] + s7[i+3:]
140-
count++
141-
}
133+
count := 0
134+
// 去掉 --- 这种情况,这种情况是由于题目标题中包含 - ,左右有空格,左右一填充,造成了 ---,3 个 -
135+
for i := 0; i < len(s7)-2; i++ {
136+
if s7[i] == '-' && s7[i+1] == '-' && s7[i+2] == '-' {
137+
fmt.Printf("【需要修正 --- 的标题是 %v】\n", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
138+
s7 = s7[:i+1] + s7[i+3:]
139+
count++
142140
}
143-
if count > 0 {
144-
fmt.Printf("总共修正了 %v 个标题\n", count)
141+
}
142+
if count > 0 {
143+
fmt.Printf("总共修正了 %v 个标题\n", count)
144+
}
145+
// 去掉 -- 这种情况,这种情况是由于题目标题中包含负号 -
146+
for i := 0; i < len(s7)-2; i++ {
147+
if s7[i] == '-' && s7[i+1] == '-' {
148+
fmt.Printf("【需要修正 -- 的标题是 %v】\n", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
149+
s7 = s7[:i+1] + s7[i+2:]
150+
count++
145151
}
152+
}
153+
if count > 0 {
154+
fmt.Printf("总共修正了 %v 个标题\n", count)
155+
}
156+
if internal {
146157
tmp.SolutionPath = fmt.Sprintf("[Go]({{< relref \"/ChapterFour/%v/%v.md\" >}})", util.GetChpaterFourFileNum(int(row.FrontendQuestionID)), fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
147158
} else {
148159
tmp.SolutionPath = fmt.Sprintf("[Go](https://books.halfrost.com/leetcode/ChapterFour/%v/%v)", util.GetChpaterFourFileNum(int(row.FrontendQuestionID)), fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package leetcode
2+
3+
import "strings"
4+
5+
// 解法一 滑动窗口
6+
func longestSubstring(s string, k int) int {
7+
res := 0
8+
for t := 1; t <= 26; t++ {
9+
freq, total, lessK, left, right := [26]int{}, 0, 0, 0, -1
10+
for left < len(s) {
11+
if right+1 < len(s) && total <= t {
12+
if freq[s[right+1]-'a'] == 0 {
13+
total++
14+
lessK++
15+
}
16+
freq[s[right+1]-'a']++
17+
if freq[s[right+1]-'a'] == k {
18+
lessK--
19+
}
20+
right++
21+
} else {
22+
if freq[s[left]-'a'] == k {
23+
lessK++
24+
}
25+
freq[s[left]-'a']--
26+
if freq[s[left]-'a'] == 0 {
27+
total--
28+
lessK--
29+
}
30+
left++
31+
}
32+
if lessK == 0 {
33+
res = max(res, right-left+1)
34+
}
35+
36+
}
37+
}
38+
return res
39+
}
40+
41+
func max(a, b int) int {
42+
if a > b {
43+
return a
44+
}
45+
return b
46+
}
47+
48+
// 解法二 递归分治
49+
func longestSubstring1(s string, k int) int {
50+
if s == "" {
51+
return 0
52+
}
53+
freq, split, res := [26]int{}, byte(0), 0
54+
for _, ch := range s {
55+
freq[ch-'a']++
56+
}
57+
for i, c := range freq[:] {
58+
if 0 < c && c < k {
59+
split = 'a' + byte(i)
60+
break
61+
}
62+
}
63+
if split == 0 {
64+
return len(s)
65+
}
66+
for _, subStr := range strings.Split(s, string(split)) {
67+
res = max(res, longestSubstring1(subStr, k))
68+
}
69+
return res
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package leetcode
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
type question395 struct {
9+
para395
10+
ans395
11+
}
12+
13+
// para 是参数
14+
// one 代表第一个参数
15+
type para395 struct {
16+
s string
17+
k int
18+
}
19+
20+
// ans 是答案
21+
// one 代表第一个答案
22+
type ans395 struct {
23+
one int
24+
}
25+
26+
func Test_Problem395(t *testing.T) {
27+
28+
qs := []question395{
29+
30+
{
31+
para395{"aaabb", 3},
32+
ans395{3},
33+
},
34+
35+
{
36+
para395{"ababbc", 2},
37+
ans395{5},
38+
},
39+
}
40+
41+
fmt.Printf("------------------------Leetcode Problem 395------------------------\n")
42+
43+
for _, q := range qs {
44+
_, p := q.ans395, q.para395
45+
fmt.Printf("【input】:%v 【output】:%v\n", p, longestSubstring(p.s, p.k))
46+
}
47+
fmt.Printf("\n\n\n")
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# [395. Longest Substring with At Least K Repeating Characters](https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/)
2+
3+
4+
## 题目
5+
6+
Given a string `s` and an integer `k`, return *the length of the longest substring of* `s` *such that the frequency of each character in this substring is greater than or equal to* `k`.
7+
8+
**Example 1:**
9+
10+
```
11+
Input: s = "aaabb", k = 3
12+
Output: 3
13+
Explanation: The longest substring is "aaa", as 'a' is repeated 3 times.
14+
```
15+
16+
**Example 2:**
17+
18+
```
19+
Input: s = "ababbc", k = 2
20+
Output: 5
21+
Explanation: The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
22+
```
23+
24+
**Constraints:**
25+
26+
- `1 <= s.length <= 10^4`
27+
- `s` consists of only lowercase English letters.
28+
- `1 <= k <= 10^5`
29+
30+
## 题目大意
31+
32+
给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。
33+
34+
## 解题思路
35+
36+
- 最容易想到的思路是递归。如果某个字符出现次数大于 0 小于 k,那么包含这个字符的子串都不满足要求。所以按照这个字符来切分整个字符串,满足题意的最长子串一定不包含切分的字符。切分完取出最长子串即可。时间复杂度 O(26*n),空间复杂度 O(26^2)
37+
- 此题另外一个思路是滑动窗口。有一个需要解决的问题是右窗口移动的条件。此题要求最长字符串,那么这个最终的字符串内包含的字符种类最多是 26 种。字符种类就是右窗口移动的条件。依次枚举字符种类,如果当前窗口内的字符种类小于当前枚举的字符种类,那么窗口右移,否则左移。窗口移动中需要动态维护 freq 频次数组。可以每次都循环一遍这个数组,计算出出现次数大于 k 的字符。虽然这种做法只最多循环 26 次,但是还是不高效。更高效的做法是维护 1 个值,一个用来记录当前出现次数小于 k 次的字符种类数 `less`。如果 freq 为 0 ,说明小于 k 次的字符种类数要发生变化,如果是右窗口移动,那么 `less++`,如果是左窗口移动,那么`less--`。同理,如果 freq 为 k ,说明小于 k 次的字符种类数要发生变化,如果是右窗口移动,那么 `less--`,如果是左窗口移动,那么`less++`。在枚举 26 个字符种类中,动态维护记录出最长字符串。枚举完成,最长字符串长度也就求出来了。时间复杂度 O(26*n),空间复杂度 O(26)
38+
39+
## 代码
40+
41+
```go
42+
package leetcode
43+
44+
import "strings"
45+
46+
// 解法一 滑动窗口
47+
func longestSubstring(s string, k int) int {
48+
res := 0
49+
for t := 1; t <= 26; t++ {
50+
freq, total, lessK, left, right := [26]int{}, 0, 0, 0, -1
51+
for left < len(s) {
52+
if right+1 < len(s) && total <= t {
53+
if freq[s[right+1]-'a'] == 0 {
54+
total++
55+
lessK++
56+
}
57+
freq[s[right+1]-'a']++
58+
if freq[s[right+1]-'a'] == k {
59+
lessK--
60+
}
61+
right++
62+
} else {
63+
if freq[s[left]-'a'] == k {
64+
lessK++
65+
}
66+
freq[s[left]-'a']--
67+
if freq[s[left]-'a'] == 0 {
68+
total--
69+
lessK--
70+
}
71+
left++
72+
}
73+
if lessK == 0 {
74+
res = max(res, right-left+1)
75+
}
76+
77+
}
78+
}
79+
return res
80+
}
81+
82+
func max(a, b int) int {
83+
if a > b {
84+
return a
85+
}
86+
return b
87+
}
88+
89+
// 解法二 递归分治
90+
func longestSubstring1(s string, k int) int {
91+
if s == "" {
92+
return 0
93+
}
94+
freq, split, res := [26]int{}, byte(0), 0
95+
for _, ch := range s {
96+
freq[ch-'a']++
97+
}
98+
for i, c := range freq[:] {
99+
if 0 < c && c < k {
100+
split = 'a' + byte(i)
101+
break
102+
}
103+
}
104+
if split == 0 {
105+
return len(s)
106+
}
107+
for _, subStr := range strings.Split(s, string(split)) {
108+
res = max(res, longestSubstring1(subStr, k))
109+
}
110+
return res
111+
}
112+
```

website/content/ChapterFour/0300~0399/0394.Decode-String.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,5 @@ func decodeString(s string) string {
8484
----------------------------------------------
8585
<div style="display: flex;justify-content: space-between;align-items: center;">
8686
<p><a href="https://books.halfrost.com/leetcode/ChapterFour/0300~0399/0393.UTF-8-Validation/">⬅️上一页</a></p>
87-
<p><a href="https://books.halfrost.com/leetcode/ChapterFour/0300~0399/0397.Integer-Replacement/">下一页➡️</a></p>
87+
<p><a href="https://books.halfrost.com/leetcode/ChapterFour/0300~0399/0395.Longest-Substring-with-At-Least-K-Repeating-Characters/">下一页➡️</a></p>
8888
</div>

0 commit comments

Comments
 (0)