leetcode

day1

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

1
2
3
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2:

1
2
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

1
2
输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

自己写的

单纯的C语言思想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func twoSum(nums []int, target int) []int {
length := len(nums)
for i := 0; i < length; i++ {
for j := 1; j < length; j++ {
if i == j {
continue
}
if nums[i]+nums[j] == target {
return []int{i, j}
}
}
}
return nil
}

主要就是靠暴力,因为是两数之和,所以两层循环解决

官方暴力解法

1
2
3
4
5
6
7
8
9
10
11
func twoSum(nums []int, target int) []int {
length := len(nums)
for k, v := range nums {
for j := k + 1; j < length; j++ {
if v+nums[j] == target {
return []int{k, j}
}
}
}
return nil
}

官方的更优解法

直接使用哈希表

1
2
3
4
5
6
7
8
思路及算法

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N) 降低到 O(1)。

这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

1
2
3
4
5
6
7
8
9
10
func twoSum(nums []int, target int) []int {
hashTable := map[int]int{}
for i, x := range nums {
if p, ok := hashTable[target-x]; ok {
return []int{p, i}
}
hashTable[x] = i
}
return nil
}

day2

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

1
2
3
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

1
2
3
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

1
2
3
4
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

chatgpt给出的答案:

用滑动窗口的方法:

1
2
3
4
5
6
7
算法思路:

定义两个指针 leftright,表示当前子串的左右边界。
用一个哈希表(map)来存储当前窗口中每个字符及其出现的位置。
不断移动 right 指针,将字符加入窗口,并更新哈希表。
当窗口中出现重复字符时,移动 left 指针,缩小窗口,直到窗口中没有重复字符为止。
在整个过程中,记录窗口的最大长度即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func lengthOfLongestSubstring(s string) int {
charMap := make(map[byte]int) // 用于存储当前窗口中每个字符及其出现的位置
maxLength := 0
left := 0

for right := 0; right < len(s); right++ {
if index, found := charMap[s[right]]; found {
// 如果字符在窗口中已经出现过,则更新 left 指针,移动到重复字符的下一个位置
left = max(left, index+1)
}
// 更新字符的最新位置
charMap[s[right]] = right
// 计算当前窗口的长度并更新最大长度
maxLength = max(maxLength, right-left+1)
}

return maxLength
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

官方解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func lengthOfLongestSubstring(s string) int {
// 哈希集合,记录每个字符是否出现过
m := map[byte]int{}
n := len(s)
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans := -1, 0
for i := 0; i < n; i++ {
if i != 0 {
// 左指针向右移动一格,移除一个字符
delete(m, s[i-1])
}
for rk+1 < n && m[s[rk+1]] == 0 {
// 不断地移动右指针
m[s[rk+1]]++
rk++
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk-i+1)
}
return ans
}

func max(x, y int) int {
if x < y {
return y
}
return x
}

day3

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

img

1
2
3
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

1
2
输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

1
2
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

自己写的

可以通过大部分测试用例,但是当输入过大无法用int表示时会溢出,导致错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
num1, num2, dig := 0, 0, 0
digit := 0
for l1 != nil {
num1 += l1.Val * int(math.Pow(10, float64(dig)))
dig++
l1 = l1.Next
}
dig = 0
for l2 != nil {
num2 += l2.Val * int(math.Pow(10, float64(dig)))
dig++
l2 = l2.Next
}
res := num1 + num2
var head *ListNode // 链表头节点
var tail *ListNode // 链表尾节点
if res == 0 {
head = &ListNode{Val: res}
return head
}
for res > 0 {
res, digit = res/10, res%10
newNode := &ListNode{Val: digit}
if head == nil {
head = newNode
tail = newNode
} else {
tail.Next = newNode
tail = newNode
}
}
return head
}

官方提供的方法:

直接把相同的位数相加,然后计算好进位处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
sum, carry := 0, 0
var head *ListNode // 链表头节点
var tail *ListNode // 链表尾节点
for l1 != nil || l2 != nil {
n1, n2 := 0, 0
if l1 != nil {
n1 = l1.Val
l1 = l1.Next
}

if l2 != nil {
n2 = l2.Val
l2 = l2.Next
}
sum = n1 + n2 + carry
sum, carry = sum%10, sum/10
newNode := &ListNode{Val: sum}
if head == nil {
head = newNode
tail = newNode
} else {
tail.Next = newNode
tail = newNode
}
}
if carry > 0 {
tail.Next = &ListNode{Val: carry}
}
return head
}

day4

4. 寻找两个正序数组的中位数

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

1
2
3
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

1
2
3
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

首先这是自己写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
res := make([]int, 0, len(nums1)+len(nums2))
i, j := 0, 0
for i < len(nums1) && j < len(nums2) {
if nums1[i] < nums2[j] {
res = append(res, nums1[i])
i++
} else {
res = append(res, nums2[j])
j++
}
}

for i < len(nums1) {
res = append(res, nums1[i])
i++
}
for j < len(nums2) {
res = append(res, nums2[j])
j++
}
return median(res)
}
func median(s []int) float64 {
if len(s)%2 == 0 {
return float64(s[len(s)/2]+s[(len(s)/2)-1]) / 2.0
} else {
return float64(s[(len(s) / 2)])
}
}

经过chatgpt优化之后,消耗的时间大大减小了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
m, n := len(nums1), len(nums2)
totalLen := m + n
res := make([]int, totalLen)

i, j, k := 0, 0, 0

for i < m && j < n {
if nums1[i] < nums2[j] {
res[k] = nums1[i]
i++
} else {
res[k] = nums2[j]
j++
}
k++
}

for i < m {
res[k] = nums1[i]
i++
k++
}
for j < n {
res[k] = nums2[j]
j++
k++
}

if totalLen%2 == 0 {
return float64(res[totalLen/2]+res[(totalLen/2)-1]) / 2.0
} else {
return float64(res[totalLen/2])
}
}

由于题目要求算法的时间复杂度为log(m+n),所以会联想到二分法

官方给的wp,将寻找中位数的操作转换为寻找第K小的数,不断地缩小数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
totalLength := len(nums1) + len(nums2)
if totalLength%2 == 1 { // 总长度为奇数
midIndex := totalLength / 2
return float64(getKthElement(nums1, nums2, midIndex+1))
} else { // 总长度为偶数
midIndex1, midIndex2 := totalLength/2-1, totalLength/2
return float64(getKthElement(nums1, nums2, midIndex1+1)+getKthElement(nums1, nums2, midIndex2+1)) / 2.0
}
return 0
}

func getKthElement(nums1, nums2 []int, k int) int {
index1, index2 := 0, 0
for {
if index1 == len(nums1) {
return nums2[index2+k-1]
}
if index2 == len(nums2) {
return nums1[index1+k-1]
}
if k == 1 {
return min(nums1[index1], nums2[index2])
}
half := k / 2
newIndex1 := min(index1+half, len(nums1)) - 1
newIndex2 := min(index2+half, len(nums2)) - 1
pivot1, pivot2 := nums1[newIndex1], nums2[newIndex2]
if pivot1 <= pivot2 {
k -= newIndex1 - index1 + 1
index1 = newIndex1 + 1
} else {
k -= newIndex2 - index2 + 1
index2 = newIndex2 + 1
}
}
return 0
}

func min(x, y int) int {
if x < y {
return x
}
return y
}

day5

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

1
2
3
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

1
2
输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

用动态规划直接开始做,时间复杂度相较于暴力的O(n^3) 降低到了O(n^2),原因就是中间那一段判断是否为回文的情况从 O(n) 优化到了O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func longestPalindrome(s string) string {
n := len(s)
if n <= 1 {
return s
}
dp := make([][]bool, n)
// 记录起始位置和字串长度
start, maxLen := -1, 0

for i := range dp {
dp[i] = make([]bool, n)
dp[i][i] = true // 单个字符是回文
}
start, maxLen = 0, 1

for Len := 2; Len <= n; Len++ { // 子串长度从2开始
for i := 0; i+Len <= n; i++ { // 左边界
j := i + Len - 1 // 右边界
if s[i] != s[j] {
continue
}
if Len > 2 && !dp[i+1][j-1] {
continue
}
dp[i][j] = true
start = i
maxLen = Len
}
}
return s[start : start+maxLen]
}

中心拓展法

枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值,即可得到最终的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func expandAroundCenter(s string, left int, right int) string {
for left >= 0 && right < len(s) && s[left] == s[right] {
left--
right++
}
return s[left+1 : right]
}

func longestPalindrome(s string) string {
n := len(s)
if n <= 1 {
return s
}

result := ""

for i := 0; i < n; i++ {
// 以当前字符为中心的奇数长度回文子串
oddPalin := expandAroundCenter(s, i, i)
// 以当前字符和下一个字符的中间为中心的偶数长度回文子串
evenPalin := expandAroundCenter(s, i, i+1)

if len(oddPalin) > len(result) {
result = oddPalin
}
if len(evenPalin) > len(result) {
result = evenPalin
}
}

return result
}

day6

6. N 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

1
2
3
P   A   H   N
A P L S I I G
Y I R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

1
string convert(string s, int numRows);

示例 1:

1
2
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例 2:

1
2
3
4
5
6
7
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I

示例 3:

1
2
输入:s = "A", numRows = 1
输出:"A"

提示:

  • 1 <= s.length <= 1000
  • s 由英文字母(小写和大写)、',''.' 组成
  • 1 <= numRows <= 1000

主要的思路就是用一个[]string保存这个字符串,再定义一个方向变量和行数变量,有点像邻接链表的图

image-20230811114859725

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func convert(s string, numRows int) (ans string) {
n := len(s)
if numRows == 1 || n < numRows {
ans = s
return
}

row, dir := 0, false
rows := make([]string, numRows)

for _, v := range s {
rows[row] += string(v)
if row == 0 || row == numRows-1 {
dir = !dir
}
if dir {
row++
} else {
row--
}
}

for _, v := range rows {
ans += v
}
return
}

day7

7. 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:

1
2
输入:x = 123
输出:321

示例 2:

1
2
输入:x = -123
输出:-321

示例 3:

1
2
输入:x = 120
输出:21

示例 4:

1
2
输入:x = 0
输出:0

提示:

  • -231 <= x <= 231 - 1

初步解答:

主要的想法是把这个整数当作字符串处理,然后逆序输出,时间复杂度为O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func rev(str string) int {
bytes := []byte(str)
n := len(bytes)
for i := 0; i < n/2; i++ {
tmp := bytes[n-i-1]
bytes[n-i-1] = bytes[i]
bytes[i] = tmp
}
str = string(bytes)
num, _ := strconv.Atoi(str)
return num
}

func reverse(x int) int {
str := strconv.Itoa(x)
if x >= 0 {
num := rev(str)
if num > math.MaxInt32 {
return 0
}
return num
} else {
// 去掉符号
str = str[1:]
num := rev(str)
if num > math.MaxInt32 {
return 0
}
return -num
}
}

官方给的答案:

妙,直接用循环逆序了

1
2
3
4
5
6
7
8
9
10
11
12
func reverse(x int) (rev int) {
for x != 0 {
if rev < math.MinInt32/10 || rev > math.MaxInt32/10 {
return 0
}
digit := x % 10
x /= 10
rev = rev*10 + digit
}
return
}

day8

8. 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,”123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1
  6. 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ' '
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
1 步:"42"(当前没有读入字符,因为没有前导空格)
^
2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+'
^
3 步:"42"(读入 "42"
^
解析得到整数 42
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42

示例 2:

1
2
3
4
5
6
7
8
9
10
11
输入:s = "   -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 "42")
^
解析得到整数 -42
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42

示例 3:

1
2
3
4
5
6
7
8
9
10
11
输入:s = "4193 with words"
输出:4193
解释:
1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
^
2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+'
^
3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193
由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193

提示:

  • 0 <= s.length <= 200
  • s 由英文字母(大写和小写)、数字(0-9)、' ''+''-''.' 组成

题目坑挺多,写出来倒是很简单,就是和示例中给的三步一样,写出对应的代码就行,就是最后的溢出判断那里,不能放在循环外面,要不然会错,leetcode还有更高阶的做法,直接引入一个有限状态机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func myAtoi(s string) (ans int) {
ans = 0
n := len(s)
i, sign := 0, 1
// 去除前导空格
for ; i < n && s[i] == ' '; i++ {
}
if i == n {
return 0
}
// 读入字符
if s[i] == '+' {
i++
} else if s[i] == '-' {
sign = -1
i++
}
// 读入数字
for j := i; j < n && s[j] >= '0' && s[j] <= '9'; j++ {
ans = ans*10 + sign*int(s[j]-'0')
// 溢出判断
if ans < 0 && ans <= math.MinInt32 {
ans = math.MinInt32
}

if ans > 0 && ans >= math.MaxInt32 {
ans = math.MaxInt32
}
}
return
}

day9

9. 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

  • 例如,121 是回文,而 123 不是。

示例 1:

1
2
输入:x = 121
输出:true

示例 2:

1
2
3
输入:x = -121
输出:false
解释:从左向右读,-121 。 从右向左读,121- 。因此它不是一个回文数。

示例 3:

1
2
3
输入:x = 10
输出:false
解释:从右向左读,01 。因此它不是一个回文数。

提示:

  • -231 <= x <= 231 - 1

可知直接把整数转换为字符串,然后分成奇数和偶数两种情况,奇数就从中间一位的两边开始对比,偶数也类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
func isPalindrome(x int) bool {
if x < 0 {
return false
}
str := strconv.Itoa(x)
count, n := 0, len(str)
if n%2 != 0 {
j := n/2 + 1
for i := n/2 - 1; i >= 0; i-- {
if j >= n {
break
}
if str[i] == str[j] {
count++
}
j++
}
if count*2 == n-1 {
return true
} else {
return false
}

}

j := n / 2
for i := n/2 - 1; i >= 0; i-- {
if j >= n {
break
}
if str[i] == str[j] {
count++
}
j++
}
if count*2 == n {
return true
}
return false
}

不转换为字符串如下:

1、首先所有的负数都不是回文数

2、以0结尾的数,除了0都不是回文数

1
2
3
4
5
6
7
8
9
10
11
func isPalindrome(x int) bool {
if x < 0 || (x % 10 == 0 && x !=0) {
return false
}
rev := 0
for x > rev {
rev = rev * 10 + x % 10 // 获得后几位的倒序
x /= 10
}
return x == rev || x == rev / 10 // rev/10是奇数,另一个是偶数
}

day10

10. 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

1
2
3
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

1
2
3
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

1
2
3
输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。

提示:

  • 1 <= s.length <= 20
  • 1 <= p.length <= 20
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 .*
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

leetcode官方题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func isMatch(s string, p string) bool {
n, m := len(s), len(p)

f := make([][]bool, n+1)
for i := 0; i <= n; i++ {
f[i] = make([]bool, m+1)
}

matches := func(i, j int) bool {
if i == 0 {
return false
}
if p[j-1] == '.' {
return true
}
return s[i-1] == p[j-1]
}

f[0][0] = true

for i := 0; i <= n; i++ {
for j := 1; j <= m; j++ {
if p[j-1] == '*' {
f[i][j] = f[i][j] || f[i][j-2]
if matches(i, j-1) {
f[i][j] = f[i][j] || f[i-1][j]
}
} else if matches(i, j) {
f[i][j] = f[i][j] || f[i-1][j-1]
}
}
}
return f[n][m]
}

day11

11. 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

img

1
2
3
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49

示例 2:

1
2
输入:height = [1,1]
输出:1

提示:

  • n == height.length
  • 2 <= n <= 105
  • 0 <= height[i] <= 104

直接双指针枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func max(a, b int) int {
if a < b {
return b
}
return a
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

func maxArea(height []int) int {
ans := 0
l, r := 0, len(height)-1
for l < r {
ans = max(ans, min(height[l], height[r])*(r-l))
if height[l] < height[r] {
l++
} else {
r--
}
}
return ans
}

day12

12.整数转罗马数字

罗马数字包含以下七种字符: IVXLCDM

1
2
3
4
5
6
7
8
字符          数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给你一个整数,将其转为罗马数字。

示例 1:

1
2
输入: num = 3
输出: "III"

示例 2:

1
2
输入: num = 4
输出: "IV"

示例 3:

1
2
输入: num = 9
输出: "IX"

示例 4:

1
2
3
输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

示例 5:

1
2
3
输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= num <= 3999

官方解法之一:

首先是将所有特殊情况都枚举出来,然后利用循环直接遍历,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var valueSymbols = []struct {
value int
symbol string
}{
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"},
}

func intToRoman(num int) string {
roman := []byte{}
for _, vs := range valueSymbols {
for num >= vs.value {
num -= vs.value
roman = append(roman, vs.symbol...)
}
if num == 0 {
break
}
}
return string(roman)
}

官方解法之二:

因为题目已经告诉了1 <= num <= 3999这个条件,所以这里直接把每一位会出现的情况全部枚举出来,然后利用运算得到每一位的数字对应的符号,然后拼接起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var (
thousands = []string{"", "M", "MM", "MMM"}
hundreds = []string{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}
tens = []string{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}
ones = []string{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}
)

func intToRoman(num int) string {
return thousands[num/1000] + hundreds[num%1000/100] + tens[num%100/10] + ones[num%10]
}
// thousand: num/1000
// hundred: num%1000/100
// ten: num%100/10
// one: num%10

day13 (Leetcode 75 Start)

1768.交替合并字符串

给你两个字符串 word1word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。

返回 合并后的字符串

示例 1:

1
2
3
4
5
6
输入:word1 = "abc", word2 = "pqr"
输出:"apbqcr"
解释:字符串合并情况如下所示:
word1: a b c
word2: p q r
合并后: a p b q c r

示例 2:

1
2
3
4
5
6
输入:word1 = "ab", word2 = "pqrs"
输出:"apbqrs"
解释:注意,word2 比 word1 长,"rs" 需要追加到合并后字符串的末尾。
word1: a b
word2: p q r s
合并后: a p b q r s

示例 3:

1
2
3
4
5
6
输入:word1 = "abcd", word2 = "pq"
输出:"apbqcd"
解释:注意,word1 比 word2 长,"cd" 需要追加到合并后字符串的末尾。
word1: a b c d
word2: p q
合并后: a p b q c d

提示:

  • 1 <= word1.length, word2.length <= 100
  • word1word2 由小写英文字母组成

非常简单的题,可以直接用下标代替append, 加快时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func mergeAlternately(word1 string, word2 string) string {
n, m := len(word1), len(word2)
res := make([]byte, n+m)
i, l := 0, 0
for i < n || i < m {
if i < n {
res[l] = word1[i]
l++
}
if i < m {
res[l] = word2[i]
l++
}
i++
}
return string(res)
}

day14

1071.字符串的最大公因子

对于字符串 st,只有在 s = t + ... + tt 自身连接 1 次或多次)时,我们才认定 “t 能除尽 s”。

给定两个字符串 str1str2 。返回 最长字符串 x,要求满足 x 能除尽 str1x 能除尽 str2

示例 1:

1
2
输入:str1 = "ABCABC", str2 = "ABC"
输出:"ABC"

示例 2:

1
2
输入:str1 = "ABABAB", str2 = "ABAB"
输出:"AB"

示例 3:

1
2
输入:str1 = "LEET", str2 = "CODE"
输出:""

提示:

  • 1 <= str1.length, str2.length <= 1000
  • str1str2 由大写英文字母组成

具体的做法有点像辗转相除法

以示例1为例:

str1 str2 operation
ABCABC ABC str1-str2
ABC ABC str1-str2
“” ABC swap(str1, str2)
ABC “” return str1

示例2:

str1 str2 operation
ABABAB ABAB str1-str2
AB ABAB swap(str1, str2)
ABAB AB str1-str2
AB AB str1-str2
“” AB swap(str1, str2)
AB “” return str1

示例3:

str1 str2 operation
LEET CODE return “”

由于str1[0:len(str2)] != str2,所有说明没有公因子,直接返回””

整个过程保证str1的长度要大于str2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func gcdOfStrings(str1 string, str2 string) string {
n, m := len(str1), len(str2)
// 确保str1的长度要大于str2
if n < m {
return gcdOfStrings(str2, str1)
}
// 如果2是空串了就返回str1
if str2 == "" {
return str1
}
if str1[:m] != str2 {
return ""
}
return gcdOfStrings(str1[m:], str2)
}

day15

1431.拥有最多糖果的孩子

给你一个数组 candies 和一个整数 extraCandies ,其中 candies[i] 代表第 i 个孩子拥有的糖果数目。

对每一个孩子,检查是否存在一种方案,将额外的 extraCandies 个糖果分配给孩子们之后,此孩子有 最多 的糖果。注意,允许有多个孩子同时拥有 最多 的糖果数目。

示例 1:

1
2
3
4
5
6
7
8
输入:candies = [2,3,5,1,3], extraCandies = 3
输出:[true,true,true,false,true]
解释:
孩子 1 2 个糖果,如果他得到所有额外的糖果(3个),那么他总共有 5 个糖果,他将成为拥有最多糖果的孩子。
孩子 2 3 个糖果,如果他得到至少 2 个额外糖果,那么他将成为拥有最多糖果的孩子。
孩子 3 5 个糖果,他已经是拥有最多糖果的孩子。
孩子 4 1 个糖果,即使他得到所有额外的糖果,他也只有 4 个糖果,无法成为拥有糖果最多的孩子。
孩子 5 3 个糖果,如果他得到至少 2 个额外糖果,那么他将成为拥有最多糖果的孩子。

示例 2:

1
2
3
4
输入:candies = [4,2,1,1
,2], extraCandies = 1
输出:[true,false,false,false,false]
解释:只有 1 个额外糖果,所以不管额外糖果给谁,只有孩子 1 可以成为拥有糖果最多的孩子。

示例 3:

1
2
输入:candies = [12,1,12], extraCandies = 10
输出:[true,false,true]

提示:

  • 2 <= candies.length <= 100
  • 1 <= candies[i] <= 100
  • 1 <= extraCandies <= 50

题意很简单,我这里用了两个循环,第一个先找到最大值,然后依次比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func kidsWithCandies(candies []int, extraCandies int) []bool {
max, n := 0, len(candies)
res := make([]bool, n)
for _, v := range candies {
if v >= max {
max = v
}
}
for k, v := range candies {
if v+extraCandies >= max {
res[k] = true
}
}
return res
}

看到官方题解学到了,我可以直接res[k] = v+extraCandies >= maxCandies,不用判断后再赋值了

1
2
3
4
5
6
7
8
9
10
11
12
13
func kidsWithCandies(candies []int, extraCandies int) []bool {
maxCandies, n := 0, len(candies)
res := make([]bool, n)
for _, v := range candies {
if v >= maxCandies {
maxCandies = v
}
}
for k, v := range candies {
res[k] = v+extraCandies >= maxCandies
}
return res
}

day16

605.种花问题

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 01 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false

示例 1:

1
2
输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

示例 2:

1
2
输入:flowerbed = [1,0,0,0,1], n = 2
输出:false

提示:

  • 1 <= flowerbed.length <= 2 * 104
  • flowerbed[i]01
  • flowerbed 中不存在相邻的两朵花
  • 0 <= n <= flowerbed.length

直接列举出所有可能的情况,然后分别作出判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func canPlaceFlowers(flowerbed []int, n int) bool {
length := len(flowerbed)
if length == 0 {
return false
}
count := 0
// 遍历
for i := 0; i < length; i++ {
if flowerbed[i] == 0 {
// 第i个位置没有种花
if i == 0 && length == 1 {
flowerbed[i] = 1
count++
} else if i == 0 && length > 1 && flowerbed[i+1] == 0 {
flowerbed[i] = 1
count++
} else if i == length-1 && flowerbed[i-1] == 0 {
flowerbed[i] = 1
count++
} else if i > 0 && i < length && flowerbed[i-1] == 0 && flowerbed[i+1] == 0 {
flowerbed[i] = 1
count++
}
}
if count >= n {
return true
}
}
return false
}

day17

345.反转字符串中的元音字母

给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。

元音字母包括 'a''e''i''o''u',且可能以大小写两种形式出现不止一次。

示例 1:

1
2
输入:s = "hello"
输出:"holle"

示例 2:

1
2
输入:s = "leetcode"
输出:"leotcede"

提示:

  • 1 <= s.length <= 3 * 105
  • s可打印的 ASCII 字符组成

利用双指针遍历,妙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func reverseVowels(s string) string {
res := []byte(s)
n := len(s)
i, j := 0, n-1
for i < j {
for i < n && !strings.Contains("aeiouAEIOU", string(res[i])) {
i++
}
for j > 0 && !strings.Contains("aeiouAEIOU", string(res[j])) {
j--
}
if i < j {
res[i], res[j] = res[j], res[i]
i++
j--
}
}
return string(res)
}

day18

151.反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

1
2
输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

1
2
3
输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

1
2
3
输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s至少存在一个 单词

进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

使用strings自带的库函数:

首先使用strings中的Fields函数,使用该函数可切割单个/多个空格,提取出单词

1
2
3
4
5
6
7
8
input:
"a good example"
output:
[a good example] // fmt.Println()
a // for range 读取出
good
example

1
2
3
4
5
6
7
8
9
10
func reverseWords(s string) string {
t := strings.Fields(s)
n := len(t)
for i := 0; i < n/2; i++ { //遍历数组的前半段直接交换
t[i], t[n-1-i] = t[n-1-i], t[i]
}
res := strings.Join(t, " ") //重新使用空格连接多个单词
return res
}

day19

238.除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

不要使用除法,且在 O(*n*) 时间复杂度内完成此题。

示例 1:

1
2
输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

1
2
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内

进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

首先最简单的想法就是直接暴力:

这样思路最浅显易懂,但是时间复杂度是O(n^2),显然会超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func productExceptSelf(nums []int) []int {
n := len(nums)
res := make([]int, n)
j, mul := 0, 1
for i := 0; i < n; i++ {
for k := 0; k < n; k++{
if k == i {
continue
}
if nums[k] == 0{
mul = 0
break
}
mul *= nums[k]
}
res[j] = mul
mul = 1
j++
}
return res
}

下面这种方法把整个数组分为了两部分,下标为 i 的前面部分的乘积和后面部分的乘积

例如:nums = [1,2,3,4]

前缀之积:[1, 1, 2, 6]

后缀之积:[24, 12, 4, 1]

结果: [24, 12, 8, 6]

但是如果用两个数组分别保存前缀和后缀的话,空间复杂度会达到 O(n^2),所以可以直接把前缀保存在res数组中,然后用tmp变量保存后缀之积,直接与对应的res相乘,就可以达到O(1)的空间复杂度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func productExceptSelf(nums []int) []int {
n := len(nums)
res := make([]int, n)
// 先求得前缀之积
tmp := 0
for i := 0; i < n; i++ {
if i == 0 {
res[i] = 1
} else {
res[i] = nums[i-1] * res[i-1]
}
}
for i := n-1; i >= 0; i-- {
if i == n-1 {
tmp = 1
res[i] *= 1
} else {
tmp *= nums[i+1]
res[i] *= tmp
}
}

return res
}

day 20

334. 递增的三元子序列

给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false

示例 1:

1
2
3
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意

示例 2:

1
2
3
输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组

示例 3:

1
2
3
输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6

提示:

  • 1 <= nums.length <= 5 * 105
  • -231 <= nums[i] <= 231 - 1

进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?

用贪心,直接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func increasingTriplet(nums []int) bool {
n := len(nums)
if n < 3 {
return false
}
first, second := nums[0], 1 << 32 - 1
for i := 1; i < n; i++ {
num := nums[i]
if num > second {
return true
} else if num > first {
second = num
} else {
first = num
}
}
return false
}

day21

443. 压缩字符串

给你一个字符数组 chars ,请使用下述算法压缩:

从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符

  • 如果这一组长度为 1 ,则将字符追加到 s 中。
  • 否则,需要向 s 追加字符,后跟这一组的长度。

压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 1010 以上,则在 chars 数组中会被拆分为多个字符。

请在 修改完输入数组后 ,返回该数组的新长度。

你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

示例 1:

1
2
3
输入:chars = ["a","a","b","b","c","c","c"]
输出:返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"]
解释:"aa""a2" 替代。"bb""b2" 替代。"ccc""c3" 替代。

示例 2:

1
2
3
输入:chars = ["a"]
输出:返回 1 ,输入数组的前 1 个字符应该是:["a"]
解释:唯一的组是“a”,它保持未压缩,因为它是一个字符。

示例 3:

1
2
3
输入:chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:返回 4 ,输入数组的前 4 个字符应该是:["a","b","1","2"]。
解释:由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。

提示:

  • 1 <= chars.length <= 2000
  • chars[i] 可以是小写英文字母、大写英文字母、数字或符号

用双指针,本地测试结果是对的,但是上传到leetcode却不行,具体的问题写在最下面了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func compress(chars []byte) int {
n := len(chars)
if n < 2 {
return n
}
s := ""
Len, j := 0, 0
for i := 0; i < n-1; i += Len {
ch := chars[i]
j = i + 1
Len = 1
for j < n && ch == chars[j] {
Len++
j++
}
if Len == 1 {
s += string(chars[i])
} else {
s += string(chars[i]) + strconv.Itoa(Len)
}
}

chars = []byte(s) // 函数内部修改,值传递,没法影响函数外部的检验

return len(s)
}

chatgpt修改后如下,同样是使用双指针,在读取的时候要修改前面的内容是重点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func compress(chars []byte) int {
n := len(chars)
if n < 2 {
return n
}

writeIdx := 0 // 写入位置的索引
readIdx := 0 // 读取位置的索引

for readIdx < n {
ch := chars[readIdx]
count := 0

// 统计连续相同字符的数量
for readIdx < n && chars[readIdx] == ch {
readIdx++
count++
}

chars[writeIdx] = ch
writeIdx++

if count > 1 {
countStr := strconv.Itoa(count)
for i := 0; i < len(countStr); i++ {
chars[writeIdx] = countStr[i]
writeIdx++
}
}
}

return writeIdx
}

day22

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

1
2
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

1
2
输入: nums = [0]
输出: [0]

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

进阶:你能尽量减少完成的操作次数吗?

使用双指针,left指向当前已经处理好的序列的尾部,right指向待处理序列的头部。

在碰到0之前,left == right,碰到0之后,right向后移动一位,然后交换,这样就逐渐把0移动到了最后

1
2
3
4
5
6
7
8
9
10
11
12
func moveZeroes(nums []int)  {
n := len(nums)
left, right := 0, 0
for right < n {
if nums[right] != 0 {
nums[left], nums[right] = nums[right], nums[left]
left++
}
right++
}
return
}

day23

392. 判断子序列

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

示例 1:

1
2
输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:

1
2
输入:s = "axc", t = "ahbgdc"
输出:false

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 10^4
  • 两个字符串都只由小写字符组成。

直接双指针遍历

自己写的

1
2
3
4
5
6
7
8
9
10
func isSubsequence(s string, t string) bool {
n, m := len(s), len(t)
j := 0
for i := 0; i < m; i++ {
if j < n && t[i] == s[j] {
j++
}
}
return j == n
}

官方题解

1
2
3
4
5
6
7
8
9
10
11
func isSubsequence(s string, t string) bool {
n, m := len(s), len(t)
i, j := 0, 0
for i < n && j < m {
if s[i] == t[j] {
i++
}
j++
}
return i == n
}

day24

1679. K 和数对的最大数目

给你一个整数数组 nums 和一个整数 k

每一步操作中,你需要从数组中选出和为 k 的两个整数,并将它们移出数组。

返回你可以对数组执行的最大操作数。

示例 1:

1
2
3
4
5
6
输入:nums = [1,2,3,4], k = 5
输出:2
解释:开始时 nums = [1,2,3,4]:
- 移出 1 4 ,之后 nums = [2,3]
- 移出 2 3 ,之后 nums = []
不再有和为 5 的数对,因此最多执行 2 次操作。

示例 2:

1
2
3
4
5
输入:nums = [3,1,3,4,3], k = 6
输出:1
解释:开始时 nums = [3,1,3,4,3]
- 移出前两个 3 ,之后nums = [1,4,3]
不再有和为 6 的数对,因此最多执行 1 次操作。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 1 <= k <= 109

先把数组排序,然后双指针遍历,当nums[i] + nums[j] > k, 就把右指针左移一个,反之就把左指针右移一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func maxOperations(nums []int, k int) int {
n := len(nums)
i, j := 0, n - 1
ans := 0
sort.Ints(nums)

for i < j {
tmp := nums[i] + nums[j]
if tmp > k {
j--
} else if tmp < k {
i++
} else {
i++
j--
ans++
}
}
return ans
}

day25

643. 子数组最大平均数 I

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k

请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

任何误差小于 10-5 的答案都将被视为正确答案。

示例 1:

1
2
3
输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

示例 2:

1
2
输入:nums = [5], k = 1
输出:5.00000

提示:

  • n == nums.length
  • 1 <= k <= n <= 105
  • -104 <= nums[i] <= 104

像这样一味的使用暴力是不行的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func findMaxAverage(nums []int, k int) float64 {
n := len(nums)
sum := 0
if n < 2 {
return float64(nums[0])
}
if k == n {
for i := 0; i < n; i++ {
sum += nums[i]
}
return float64(sum) / float64(k)
}

for i := 0; i <= n-k; i++ {
tmp := 0
for j := 0; j < k; j++ {
tmp += nums[i+j]

}
if tmp > sum {
sum = tmp
}
}
return float64(sum) / float64(k)
}

官方的题解是使用滑动窗口

img

img

img

img

img

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func findMaxAverage(nums []int, k int) float64 {
sum := 0
for _, v := range nums[:k] {
sum += v
}
maxSum := sum
for i := k; i < len(nums); i++ {
sum = sum - nums[i-k] + nums[i]
maxSum = max(maxSum, sum)
}
return float64(maxSum) / float64(k)
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

day25

1456. 定长子串中元音的最大数目

给你字符串 s 和整数 k

请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。

英文中的 元音字母 为(a, e, i, o, u)。

示例 1:

1
2
3
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。

示例 2:

1
2
3
输入:s = "aeiou", k = 2
输出:2
解释:任意长度为 2 的子字符串都包含 2 个元音字母。

示例 3:

1
2
3
输入:s = "leetcode", k = 3
输出:2
解释:"lee""eet""ode" 都包含 2 个元音字母。

示例 4:

1
2
3
输入:s = "rhythms", k = 4
输出:0
解释:字符串 s 中不含任何元音字母。

示例 5:

1
2
输入:s = "tryhard", k = 4
输出:1

提示:

  • 1 <= s.length <= 10^5
  • s 由小写英文字母组成
  • 1 <= k <= s.length

第一次写的,虽然结果正确但是超时了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func maxVowels(s string, k int) int {
Max := 0
i, j := 0, k-1
for j < len(s) {
count := Calc(s[i:j+1], k)
if Max < count {
Max = count
}
i++
j++
}
return Max
}

func Calc(res string, k int) (count int) {
count = 0
for i := 0; i < k; i++ {
if strings.Contains("aeiou", string(res[i])) {
count++
}
}
return
}

考虑到其实不需要每次都单独计算res中有多少个元音字母,只需要在上一个的基础上加减

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func maxVowels(s string, k int) int {
count := 0
for i := 0; i < k; i++ {
if strings.Contains("aeiou", string(s[i])) {
count++
}
}
Max := count
for i := k; i < len(s); i++ {
if strings.Contains("aeiou", string(s[i])) {
count++
}
if strings.Contains("aeiou", string(s[i-k])) {
count--
}
if count > Max {
Max = count
}
}
return Max
}
/*
时间
24ms
击败 28.44%使用 Go 的用户

内存
4.49MB
击败 31.74%使用 Go 的用户
*/

上一个使用了strings的函数判断,这样时间和空间的消耗会增加不少,下面的方法用if直接判断,进一步优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func maxVowels(s string, k int) int {
count := 0
// 计算第一个窗口的元音数量
for i := 0; i < k; i++ {
if s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u' {
count++
}
}
// 初始化最大值
maxcount := count
// 遍历
for i := k; i < len(s); i++ {
// 如果窗口第一个数为元音,减1
if s[i-k] == 'a' || s[i-k] == 'e' || s[i-k] == 'i' || s[i-k] == 'o' || s[i-k] == 'u' {
count--
}
// 如果下一个窗口的下一个数值为元音,加1
if s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u' {
count++
}
// 更新最大值
if maxcount < count {
maxcount = count
}
}
// 返回最大值
return maxcount
}
/*
时间
8ms
击败 85.33%使用 Go 的用户

内存
4.44MB
击败 75.45%使用 Go 的用户
*/

day26

1004. 最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 1 的最大个数

示例 1:

1
2
3
4
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6

示例 2:

1
2
3
4
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1
  • 0 <= k <= nums.length

chatgpt给的解释

  1. 使用两个指针 leftright 来表示一个窗口,初始时两者都指向数组的起始位置。
  2. 移动 right 指针,每次检查 nums[right] 是否为0。如果是0,表示可以进行一次翻转,将 cnt(翻转次数)加1。
  3. 如果 cnt 大于 k,说明窗口内的0已经超过了允许的翻转次数,需要移动 left 指针来缩小窗口,直到 cnt 不大于 k 为止,这样就可以继续向右移动 right 指针。
  4. 在每次移动 right 指针的过程中,都更新 res(记录连续1的最大个数),即 res 等于当前窗口的大小 right - left
  5. 重复上述步骤,直到 right 指针遍历完整个数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func longestOnes(nums []int, k int) int {
res := 0
left, right := 0, 0
cnt := 0
for right < len(nums) {
if nums[right] == 0 {
cnt++
}
right++

for cnt > k {
if nums[left] == 0 {
cnt--
}
left++
}
res = max(res, right-left)
}
return res
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

day27

1493. 删掉一个元素以后全为 1 的最长子数组

给你一个二进制数组 nums ,你需要从中删掉一个元素。

请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。

如果不存在这样的子数组,请返回 0 。

提示 1:

1
2
3
输入:nums = [1,1,0,1]
输出:3
解释:删掉位置 2 的数后,[1,1,1] 包含 3 1

示例 2:

1
2
3
输入:nums = [0,1,1,1,0,1,1,0,1]
输出:5
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1] 。

示例 3:

1
2
3
输入:nums = [1,1,1]
输出:2
解释:你必须要删除一个元素。

提示:

  • 1 <= nums.length <= 105
  • nums[i] 要么是 0 要么是 1

和上一道题完全是一模一样的做法,只不过需要在最后减1,确保只统计一个0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func longestSubarray(nums []int) int {
left, right := 0, 0
n := len(nums)
maxLen := 0
cnt := 0
for right < n {
if nums[right] == 0 {
cnt++
}
for cnt > 1 {
if nums[left] == 0 {
cnt--
}
left++
}
right++
maxLen = max(maxLen, right - left - 1)
}
return maxLen
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

day28

1732. 找到最高海拔

有一个自行车手打算进行一场公路骑行,这条路线总共由 n + 1 个不同海拔的点组成。自行车手从海拔为 0 的点 0 开始骑行。

给你一个长度为 n 的整数数组 gain ,其中 gain[i] 是点 i 和点 i + 1净海拔高度差0 <= i < n)。请你返回 最高点的海拔

示例 1:

1
2
3
输入:gain = [-5,1,5,0,-7]
输出:1
解释:海拔高度依次为 [0,-5,-4,1,1,-6] 。最高海拔为 1 。

示例 2:

1
2
3
输入:gain = [-4,-3,-2,-1,4,3,2]
输出:0
解释:海拔高度依次为 [0,-4,-7,-9,-10,-6,-3,-1] 。最高海拔为 0 。

提示:

  • n == gain.length
  • 1 <= n <= 100
  • -100 <= gain[i] <= 100

用空间复杂度为O(n)的算法直接两次遍历解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func largestAltitude(gain []int) int {
real := make([]int, len(gain)+1)
n, m := len(real), len(gain)
real[0] = 0
k := 1
for i:= 0; i < m; i++ {
if k < n {
real[k] = gain[i] + real[k-1]
k++
}
}
maxGain := 0
for i := 0; i < n; i++ {
if real[i] > maxGain {
maxGain = real[i]
}
}
return maxGain
}

空间复杂度为O(1)的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func largestAltitude(gain []int) (ans int) {
total := 0
for _, x := range gain {
total += x
ans = max(ans, total)
}
return
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

day29

724. 寻找数组的中心下标

给你一个整数数组 nums ,请计算数组的 中心下标

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1

示例 1:

1
2
3
4
5
6
输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

1
2
3
4
输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

1
2
3
4
5
6
输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0

提示:

  • 1 <= nums.length <= 104
  • -1000 <= nums[i] <= 1000

官方题解

记数组的全部元素之和为 total,
当遍历到第 i 个元素时,设其左侧元素之和为
sum,则其右侧元素之和为 total- nums; - sum。左右侧元素相等即为 sum =
total- nums; - sum,即 2 x sum + nums; = total。
当中心索引左侧或右侧没有元素时,即为零个项相加,这在数学上称作[空和]
(empty sum)。在程序设计中我们约定[空和是零]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func pivotIndex(nums []int) int {
total := 0
for _, v := range nums {
total += v
}

sum := 0

for i, v := range nums {
if 2*sum+v == total {
return i
}
sum += v
}
return -1
}

day 30

2215. 找出两数组的不同

给你两个下标从 0 开始的整数数组 nums1nums2 ,请你返回一个长度为 2 的列表 answer ,其中:

  • answer[0]nums1 中所有 存在于 nums2 中的 不同 整数组成的列表。
  • answer[1]nums2 中所有 存在于 nums1 中的 不同 整数组成的列表。

注意:列表中的整数可以按 任意 顺序返回。

示例 1:

1
2
3
4
5
输入:nums1 = [1,2,3], nums2 = [2,4,6]
输出:[[1,3],[4,6]]
解释:
对于 nums1 ,nums1[1] = 2 出现在 nums2 中下标 0 处,然而 nums1[0] = 1 和 nums1[2] = 3 没有出现在 nums2 中。因此,answer[0] = [1,3]
对于 nums2 ,nums2[0] = 2 出现在 nums1 中下标 1 处,然而 nums2[1] = 4 和 nums2[2] = 6 没有出现在 nums2 中。因此,answer[1] = [4,6]

示例 2:

1
2
3
4
5
输入:nums1 = [1,2,3,3], nums2 = [1,1,2,2]
输出:[[3],[]]
解释:
对于 nums1 ,nums1[2] 和 nums1[3] 没有出现在 nums2 中。由于 nums1[2] == nums1[3] ,二者的值只需要在 answer[0] 中出现一次,故 answer[0] = [3]
nums2 中的每个整数都在 nums1 中出现,因此,answer[1] = []

提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • -1000 <= nums1[i], nums2[i] <= 1000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func findDifference(nums1 []int, nums2 []int) [][]int {
set1, set2 := map[int]bool{}, map[int]bool{}

for _, v := range nums1 {
set1[v] = true
}

for _, v := range nums2 {
set2[v] = true
}

var a, b []int
for v := range set1 {
if !set2[v] {
a = append(a, v)
}
}

for v := range set2 {
if !set1[v] {
b = append(b, v)
}
}
return [][]int{a, b}

}

补充:go map的增删改查以及初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func main () {
// 字面量初始化
test := make[int]bool {
1: false,
2: true,
3: false,
}

// 内置函数make()初始化
test1 := make(map[string]int, 10)
test1["apple"] = 2
test1["banana"] = 3

// 增
test1["red"] = 4
// 改
test1["red"] = 5
// 删
delete(test1, "red")
// 查
v, exist := test1["red"]
// 如果"red"在map test1之中,返回的v是"red"对应的值,exist = true
// 如果"red"不在map test1之中,返回的v是0,exist = false

// 遍历
for k, v := range test1 {
fmt.Println(k, v)
}
// 输出示例:
// apple 2
// banana 3

for k := range test1 {
fmt.Println(k)
}
// 输出示例
// apple
// banana

}

day31

1207. 独一无二的出现次数

给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。

如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false

示例 1:

1
2
3
输入:arr = [1,2,2,1,1,3]
输出:true
解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。

示例 2:

1
2
输入:arr = [1,2]
输出:false

示例 3:

1
2
输入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
输出:true

提示:

  • 1 <= arr.length <= 1000
  • -1000 <= arr[i] <= 1000

官方题解

times := map[int]struct{}{}:创建一个空的整数到空结构体的映射,这个映射将用于记录不同的出现次数。times 的键是不同的出现次数,而值是空结构体。这种技巧通常用于检查集合中的唯一性,因为 Go 语言中的映射不能包含重复的键,所以通过这种方式来判断不同的出现次数是否唯一。

使用循环遍历 hash 中的值(即不同元素的出现次数),并将这些值作为键添加到 times 映射中,对应的值是一个空结构体。由于 Go 语言的映射不能包含重复的键,因此如果相同的出现次数出现多次,只有一个会被记录在 times 中。这意味着最终 times 中的键是不同的出现次数。

1
2
3
4
5
6
7
8
9
10
11
func uniqueOccurrences(arr []int) bool {
hash := map[int]int{}
for _, v := range arr {
hash[v]++
}
times := map[int]struct{}{}
for _, c := range hash {
times[c] = struct{}{}
}
return len(times) == len(hash)
}

用hash记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func uniqueOccurrences(arr []int) bool {
hash := map[int]int{}
for _, v := range arr {
hash[v]++
}
res := make(map[int]int)
for k, v := range hash {
if res[v] == 0 {
res[v] = k
} else {
return false
}
}
return true

}

day32

1657. 确定两个字符串是否接近

如果可以使用以下操作从一个字符串得到另一个字符串,则认为两个字符串 接近

  • 操作 1:交换任意两个现有字符。

    • 例如,a**b**cd**e** -> a**e**cd**b**
  • 操作 2:将一个现有字符的每次出现转换为另一个现有字符,并对另一个字符执行相同的操作。

    • 例如,**aa**c**abb** -> **bb**c**baa**(所有 a 转化为 b ,而所有的 b 转换为 a

你可以根据需要对任意一个字符串多次使用这两种操作。

给你两个字符串,word1word2 。如果 word1word2 接近 ,就返回 true ;否则,返回 false

示例 1:

1
2
3
4
5
输入:word1 = "abc", word2 = "bca"
输出:true
解释:2 次操作从 word1 获得 word2 。
执行操作 1"abc" -> "acb"
执行操作 1"acb" -> "bca"

示例 2:

1
2
3
输入:word1 = "a", word2 = "aa"
输出:false
解释:不管执行多少次操作,都无法从 word1 得到 word2 ,反之亦然。

示例 3:

1
2
3
4
5
6
输入:word1 = "cabbba", word2 = "abbccc"
输出:true
解释:3 次操作从 word1 获得 word2 。
执行操作 1"cabbba" -> "caabbb"
执行操作 2"caabbb" -> "baaccc"
执行操作 2"baaccc" -> "abbccc"

示例 4:

1
2
3
输入:word1 = "cabbba", word2 = "aabbss"
输出:false
解释:不管执行多少次操作,都无法从 word1 得到 word2 ,反之亦然。

提示:

  • 1 <= word1.length, word2.length <= 105
  • word1word2 仅包含小写英文字母

把题意翻译成充要条件就是:

  1. 两字符串的长度相同
  2. 两字符串的字符种类相同, 例如,对于某个字符,要么都有,要么都没有。

符合:word1=abbccc,word2=caabbb,都有 a、b、c

不符合:word1=abbccc,word2=abbccd,word1 只有3类字符a、b、c,无论如何转换 不出有4类字符的 word2

  1. 字符频次相同。跟具体是什么字符无关,只要频次相同即可。

符合:word1=abbccc,word2=caabbb,都有1、2、3频次

不符合,word1=abbccc,word2=aabbcc,word1 频次有1、2、3,word2 的频次只有2

所以: 解题过程实际上在验证是否满足3个条件,而不是寻求满足 word1 转换到 word2 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func closeStrings(word1 string, word2 string) bool {
n, m := len(word1), len(word2)
if n != m {
return false
}

for _, v := range word1 {
if !strings.Contains(word2, string(v)) {
return false
}
}

hash1, hash2 := map[rune]int{}, map[rune]int{}

arr1, arr2 := []int{}, []int{}

//记录每个字符出现的次数
for _, v := range word1 {
_, ok := hash1[v]
if ok {
hash1[v]++
} else {
hash1[v] = 1
}
}

for _, v := range hash1 {
arr1 = append(arr1, v)
}
sort.Ints(arr1)

for _, v := range word2 {
_, ok := hash2[v]
if ok {
hash2[v]++
} else {
hash2[v] = 1
}
}

for _, v := range hash2 {
arr2 = append(arr2, v)
}
sort.Ints(arr2)

if reflect.DeepEqual(arr1, arr2) {
return true
}
return false

}

day33

2352. 相等行列对

给你一个下标从 0 开始、大小为 n x n 的整数矩阵 grid ,返回满足 Ri 行和 Cj 列相等的行列对 (Ri, Cj) 的数目

如果行和列以相同的顺序包含相同的元素(即相等的数组),则认为二者是相等的。

示例 1:

img

1
2
3
4
输入:grid = [[3,2,1],[1,7,6],[2,7,7]]
输出:1
解释:存在一对相等行列对:
- (第 2 行,第 1 列):

示例 2:

img

1
2
3
4
5
6
输入:grid = [[3,1,2,2],[1,4,4,5],[2,4,2,2],[2,4,2,2]]
输出:3
解释:存在三对相等行列对:
- (第 0 行,第 0 列):[3,1,2,2]
- (第 2 行, 第 2 列):[2,4,2,2]
- (第 3 行, 第 2 列):[2,4,2,2]

提示:

  • n == grid.length == grid[i].length
  • 1 <= n <= 200
  • 1 <= grid[i][j] <= 105

官方题解:

首先将矩阵的行放入哈希表中统计次数,哈希表的键可以是将行拼接后的字符串,也可以用各语言内置的数据结构,然后分别统计每一列相等的行有多少,求和即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func equalPairs(grid [][]int) int {
n := len(grid)
cnt := make(map[string]int)
for _, row := range grid {
cnt[fmt.Sprint(row)]++
}
res := 0
for j := 0; j < n; j++ {
var arr []int
for i := 0; i < n; i++ {
arr = append(arr, grid[i][j])
}
if val, ok := cnt[fmt.Sprint(arr)]; ok {
res += val
}
}
return res
}

2390. 从字符串中移除星号

给你一个包含若干星号 * 的字符串 s

在一步操作中,你可以:

  • 选中 s 中的一个星号。
  • 移除星号 左侧 最近的那个 非星号 字符,并移除该星号自身。

返回移除 所有 星号之后的字符串

注意:

  • 生成的输入保证总是可以执行题面中描述的操作。
  • 可以证明结果字符串是唯一的。

示例 1:

1
2
3
4
5
6
7
输入:s = "leet**cod*e"
输出:"lecoe"
解释:从左到右执行移除操作:
- 距离第 1 个星号最近的字符是 "leet**cod*e" 中的 't' ,s 变为 "lee*cod*e"
- 距离第 2 个星号最近的字符是 "lee*cod*e" 中的 'e' ,s 变为 "lecod*e"
- 距离第 3 个星号最近的字符是 "lecod*e" 中的 'd' ,s 变为 "lecoe"
不存在其他星号,返回 "lecoe"

示例 2:

1
2
3
输入:s = "erase*****"
输出:""
解释:整个字符串都会被移除,所以返回空字符串。

提示:

  • 1 <= s.length <= 105
  • s 由小写英文字母和星号 * 组成
  • s 可以执行上述操作

根本不需要用队列,直接用切片

1
2
3
4
5
6
7
8
9
10
11
12
13
func removeStars(s string) string {
st := []rune{}

for _, v := range s {
if v == '*' {
st = st[:len(st)-1]
} else {
st = append(st, v)
}
}

return string(st)
}

day34

735. Asteroid Collision

We are given an array asteroids of integers representing asteroids in a row.

For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative meaning left). Each asteroid moves at the same speed.

Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same size, both will explode. Two asteroids moving in the same direction will never meet.

Example 1:

1
2
3
Input: asteroids = [5,10,-5]
Output: [5,10]
Explanation: The 10 and -5 collide resulting in 10. The 5 and 10 never collide.

Example 2:

1
2
3
Input: asteroids = [8,-8]
Output: []
Explanation: The 8 and -8 collide exploding each other.

Example 3:

1
2
3
Input: asteroids = [10,2,-5]
Output: [10]
Explanation: The 2 and -5 collide resulting in -5. The 10 and -5 collide resulting in 10.

Constraints:

  • 2 <= asteroids.length <= 104
  • -1000 <= asteroids[i] <= 1000
  • asteroids[i] != 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func asteroidCollision(asteroids []int) (st []int) {
for _, aster := range asteroids {
alive := true
for alive && aster < 0 && len(st) > 0 && st[len(st)-1] > 0 {
alive = st[len(st)-1] < -aster
if st[len(st)-1] <= -aster {
st = st[:len(st)-1]
}
}
if alive {
st = append(st, aster)
}
}
return
}

看了一个视频讲解:

【Leetcode】735. 行星碰撞(写了一坨屎,但是优化 )【每日一题系列20220713】(py & C++)_哔哩哔哩_bilibili

image-20230910132036401

思路清晰,用go模仿了一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func asteroidCollision(asteroids []int) (st []int) {
for _, value := range asteroids {
// 当栈为空或者是元素是正数
if value > 0 || len(st) == 0 {
st = append(st, value)
} else {
flag := true
for len(st) != 0 && value < 0 && st[len(st)-1] > 0 {
if st[len(st)-1] > -value {
flag = false
break
} else if st[len(st)-1] < -value {
st = st[:len(st)-1]
} else {
st = st[:len(st)-1]
flag = false
break
}
}
if flag {
st = append(st, value)
}
}
}
return
}

day35

394.Decode String

Given an encoded string, return its decoded string.

The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.

You may assume that the input string is always valid; there are no extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there will not be input like 3a or 2[4].

The test cases are generated so that the length of the output will never exceed 105.

Example 1:

1
2
Input: s = "3[a]2[bc]"
Output: "aaabcbc"

Example 2:

1
2
Input: s = "3[a2[c]]"
Output: "accaccacc"

Example 3:

1
2
Input: s = "2[abc]3[cd]ef"
Output: "abcabccdcdcdef"

Constraints:

  • 1 <= s.length <= 30
  • s consists of lowercase English letters, digits, and square brackets '[]'.
  • s is guaranteed to be a valid input.
  • All the integers in s are in the range [1, 300].

Simulate the stack with slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func decodeString(s string) string {
strStack := make([]string, 0)
numStack := make([]int, 0)
currentStr := ""
currentNum := 0
for i := 0; i < len(s); i++ {
char := string(s[i])
if char >= "0" && char <= "9" { // 数字
num, _ := strconv.Atoi(char)
currentNum = currentNum*10 + num
} else if char == "[" { // 左括号
// 入栈并重置
numStack = append(numStack, currentNum)
strStack = append(strStack, currentStr)
currentNum = 0
currentStr = ""
} else if char == "]" { // 右括号
// 出栈并解码
num := numStack[len(numStack)-1]
numStack = numStack[:len(numStack)-1]

prevStr := strStack[len(strStack)-1]
strStack = strStack[:len(strStack)-1]

currentStr = prevStr + strings.Repeat(currentStr, num)
} else {
currentStr += char
}
}
return currentStr
}

933. Number of Recent Calls

You have a RecentCounter class which counts the number of recent requests within a certain time frame.

Implement the RecentCounter class:

  • RecentCounter() Initializes the counter with zero recent requests.
  • int ping(int t) Adds a new request at time t, where t represents some time in milliseconds, and returns the number of requests that has happened in the past 3000 milliseconds (including the new request). Specifically, return the number of requests that have happened in the inclusive range [t - 3000, t].

It is guaranteed that every call to ping uses a strictly larger value of t than the previous call.

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
Input
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
Output
[null, 1, 2, 3, 3]

Explanation
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1], range is [-2999,1], return 1
recentCounter.ping(100); // requests = [1, 100], range is [-2900,100], return 2
recentCounter.ping(3001); // requests = [1, 100, 3001], range is [1,3001], return 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002], range is [2,3002], return 3

Constraints:

  • 1 <= t <= 109
  • Each test case will call ping with strictly increasing values of t.
  • At most 104 calls will be made to ping.

Simulate the queue with slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type RecentCounter struct {
request []int
}

func Constructor() RecentCounter {
return RecentCounter{}
}

func (c *RecentCounter) Ping(t int) int {
// 将新的时间戳加入队列
c.request = append(c.request, t)

// 删除超出时间范围的时间戳
for c.request[0] < t-3000 {
c.request = c.request[1:]
}
return len(c.request)
}

leetcode
http://example.com/2023/09/04/leetcode/
Author
Eutop1a
Posted on
September 4, 2023
Licensed under