1101-1200-Easy

1103.分糖果 II(3)

  • 题目

排排坐,分糖果。
我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。
给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。
然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,
依此类推,直到给最后一个小朋友 2 * n 颗糖果。
重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。
注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。
返回一个长度为 num_people、元素之和为 candies 的数组,
以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。
示例 1:输入:candies = 7, num_people = 4 输出:[1,2,3,1]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0,0]。
第三次,ans[2] += 3,数组变为 [1,2,3,0]。
第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。

示例 2:输入:candies = 10, num_people = 3 输出:[5,2,3]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0]。
第三次,ans[2] += 3,数组变为 [1,2,3]。
第四次,ans[0] += 4,最终数组变为 [5,2,3]。
提示:
    1 <= candies <= 10^9
    1 <= num_people <= 1000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 暴力法 O(n^(1/2)) O(n)
02 暴力法 O(n^(1/2)) O(n)
03 等差数列求和公式 O(n^(1/2)) O(n)
func distributeCandies(candies int, num_people int) []int {
	res := make([]int, num_people)
	i := 0
	count := 0
	for candies > 0 {
		count++
		if candies >= count {
			res[i%num_people] += count
		} else {
			res[i%num_people] += candies
		}
		i++
		candies = candies - count
	}
	return res
}

#
func distributeCandies(candies int, num_people int) []int {
	res := make([]int, num_people)
	count := 1
	for candies > 0 {
		for i := 0; i < num_people; i++ {
			if candies >= count {
				res[i] = res[i] + count
				candies = candies - count
			} else {
				res[i] = res[i] + candies
				candies = 0
			}
			count++
		}
	}
	return res
}

#
func distributeCandies(candies int, num_people int) []int {
	res := make([]int, num_people)
	times := 1
	for times*(times+1)/2 <= candies {
		times++
	}
	// 计算出当前糖果最多可以发给多少个人,剩下最后一个人多少颗糖
	times--
	last := candies - times*(times+1)/2
	for i := 0; i < num_people; i++ {
		n := times / num_people
		if times%num_people > i {
			n = n + 1
		}
		// 等差数列{an}的通项公式为:an=a1+(n-1)d。
		// 前n项和公式为:Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/2
		// Sn=n(a1+a1+(n-1)d)/2=n(2a1+(n-1)d)/2
		// (i+1)为首项,num_people为公差,n为数列长度,的等差数列的和
		res[i] = n * (2*(i+1) + (n-1)*num_people) / 2
		if times%num_people == i {
			res[i] = res[i] + last
		}
	}
	return res
}

1108.IP地址无效化(2)

  • 题目

给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。
所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 "."。
示例 1:输入:address = "1.1.1.1" 输出:"1[.]1[.]1[.]1"
示例 2:输入:address = "255.100.50.0" 输出:"255[.]100[.]50[.]0"
提示:
    给出的 address 是一个有效的 IPv4 地址
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 内置函数 O(n) O(n)
02 遍历 O(n) O(n)
func defangIPaddr(address string) string {
	return strings.ReplaceAll(address, ".", "[.]")
}

#
func defangIPaddr(address string) string {
	res := ""
	for i := range address {
		if address[i] == '.' {
			res = res + "[.]"
		} else {
			res = res + string(address[i])
		}
	}
	return res
}

1122.数组的相对排序(3)

  • 题目

给你两个数组,arr1 和 arr2,
    arr2 中的元素各不相同
    arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。
未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。
示例:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]
提示:
    arr1.length, arr2.length <= 1000
    0 <= arr1[i], arr2[i] <= 1000
    arr2 中的元素 arr2[i] 各不相同
    arr2 中的每个元素 arr2[i] 都出现在 arr1 中
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(nlog(n)) O(n)
02 暴力法 O(n^2) O(1)
03 数组辅助 O(n) O(1)
func relativeSortArray(arr1 []int, arr2 []int) []int {
	if len(arr2) == 0 {
		sort.Ints(arr1)
		return arr1
	}
	res := make([]int, 0)
	m := make(map[int]int)
	for i := range arr1 {
		m[arr1[i]]++
	}
	for i := 0; i < len(arr2); i++ {
		for j := 0; j < m[arr2[i]]; j++ {
			res = append(res, arr2[i])
		}
		m[arr2[i]] = 0
	}
	tempArr := make([]int, 0)
	for key, value := range m {
		for value > 0 {
			tempArr = append(tempArr, key)
			value--
		}
	}
	sort.Ints(tempArr)
	res = append(res, tempArr...)
	return res
}

#
func relativeSortArray(arr1 []int, arr2 []int) []int {
	count := 0
	for i := 0; i < len(arr2); i++ {
		for j := count; j < len(arr1); j++ {
			if arr2[i] == arr1[j] {
				arr1[count], arr1[j] = arr1[j], arr1[count]
				count++
			}
		}
	}
	sort.Ints(arr1[count:])
	return arr1
}

#
func relativeSortArray(arr1 []int, arr2 []int) []int {
	temp := make([]int, 1001)
	for i := range arr1 {
		temp[arr1[i]]++
	}
	count := 0
	for i := range arr2 {
		for temp[arr2[i]] > 0 {
			arr1[count] = arr2[i]
			temp[arr2[i]]--
			count++
		}
	}
	for i := 0; i < len(temp); i++ {
		for temp[i] > 0 {
			arr1[count] = i
			temp[i]--
			count++
		}
	}
	return arr1
}

1128.等价多米诺骨牌对的数量(2)

  • 题目

给你一个由一些多米诺骨牌组成的列表 dominoes。
如果其中某一张多米诺骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。
形式上,dominoes[i] = [a, b] 和 dominoes[j] = [c, d] 
等价的前提是 a==c 且 b==d,或是 a==d 且 b==c。
在 0 <= i < j < dominoes.length 的前提下,
找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。

示例:输入:dominoes = [[1,2],[2,1],[3,4],[5,6]] 输出:1
提示:
    1 <= dominoes.length <= 40000
    1 <= dominoes[i][j] <= 9
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(1)
02 数组辅助 O(n) O(1)
func numEquivDominoPairs(dominoes [][]int) int {
	m := make(map[string]int)
	for i := 0; i < len(dominoes); i++ {
		a := dominoes[i][0]
		b := dominoes[i][1]
		if a > b {
			a, b = b, a
		}
		m[fmt.Sprintf("%d,%d", a, b)]++
	}
	res := 0
	for _, v := range m {
		res = res + v*(v-1)/2
	}
	return res
}

#
func numEquivDominoPairs(dominoes [][]int) int {
	res := 0
	arr := make([]int, 101)
	for i := 0; i < len(dominoes); i++ {
		a := dominoes[i][0]
		b := dominoes[i][1]
		if a > b {
			a, b = b, a
		}
		res = res + arr[a*10+b]
		arr[a*10+b]++
	}
	return res
}

1137.第N个泰波那契数(3)

  • 题目

泰波那契序列 Tn 定义如下: 
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:输入:n = 4 输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
示例 2:
输入:n = 25
输出:1389537
提示:
    0 <= n <= 37
    答案保证是一个 32 位整数,即 answer <= 2^31 - 1。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(n)
02 递推 O(n) O(1)
03 递归 O(n) O(n)
func tribonacci(n int) int {
	arr := make([]int, n+3)
	arr[0] = 0
	arr[1] = 1
	arr[2] = 1
	for i := 3; i <= n; i++ {
		arr[i] = arr[i-1] + arr[i-2] + arr[i-3]
	}
	return arr[n]
}

#
func tribonacci(n int) int {
	if n == 0 {
		return 0
	}
	if n == 1 || n == 2 {
		return 1
	}
	a := 0
	b := 1
	c := 1
	for i := 3; i <= n; i++ {
		c, b, a = a+b+c, c, b
	}
	return c
}

#
var m map[int]int

func tribonacci(n int) int {
	if m == nil {
		m = make(map[int]int)
	}
	if n == 0 {
		return 0
	}
	if n == 1 || n == 2 {
		return 1
	}
	if value, ok := m[n]; ok {
		return value
	} else {
		value := tribonacci(n-1) + tribonacci(n-2) + tribonacci(n-3)
		m[n] = value
	}
	return m[n]
}

1154.一年中的第几天(2)

  • 题目

给你一个按 YYYY-MM-DD 格式表示日期的字符串 date,请你计算并返回该日期是当年的第几天。
通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。
每个月的天数与现行公元纪年法(格里高利历)一致。
示例 1:输入:date = "2019-01-09" 输出:9
示例 2:输入:date = "2019-02-10" 输出:41
示例 3:输入:date = "2003-03-01" 输出:60
示例 4:输入:date = "2004-03-01" 输出:61
提示:
    date.length == 10
    date[4] == date[7] == '-',其他的 date[i] 都是数字。
    date 表示的范围从 1900 年 1 月 1 日至 2019 年 12 月 31 日。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历 O(1) O(1)
02 内置函数 O(1) O(1)
func dayOfYear(date string) int {
	arr := strings.Split(date, "-")
	year, _ := strconv.Atoi(arr[0])
	month, _ := strconv.Atoi(arr[1])
	day, _ := strconv.Atoi(arr[2])
	res := 0
	for i := 0; i < month; i++ {
		switch i {
		case 1, 3, 5, 7, 8, 10, 12:
			res = res + 31
		case 4, 6, 9, 11:
			res = res + 30
		case 2:
			res = res + 28
			if year%400 == 0 || (year%4 == 0 && year%100 != 0) {
				res = res + 1
			}
		}
	}
	res = res + day
	return res
}

#
func dayOfYear(date string) int {
	format := "2006-01-02"
	t, _ := time.Parse(format, date)
	return t.YearDay()
}

1160.拼写单词(3)

  • 题目

给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),
那么我们就认为你掌握了这个单词。
注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。
示例 1:输入:words = ["cat","bt","hat","tree"], chars = "atach" 输出:6
解释:  可以形成字符串 "cat" 和 "hat",所以答案是 3 + 3 = 6。
示例 2:输入:words = ["hello","world","leetcode"], chars = "welldonehoneyr" 输出:10
解释: 可以形成字符串 "hello" 和 "world",所以答案是 5 + 5 = 10。
提示:
    1 <= words.length <= 1000
    1 <= words[i].length, chars.length <= 100
    所有字符串中都仅包含小写英文字母
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n^2) O(1)
02 遍历-内置函数 O(n^2) O(1)
03 数组辅助 O(n^2) O(1)
func countCharacters(words []string, chars string) int {
	m := make(map[byte]int)
	for i := range chars {
		m[chars[i]]++
	}
	res := 0
	for i := 0; i < len(words); i++ {
		temp := make(map[byte]int)
		flag := true
		for j := range words[i] {
			temp[words[i][j]]++
		}
		if len(temp) > len(m) {
			continue
		}
		for k, v := range temp {
			if v > m[k] {
				flag = false
				break
			}
		}
		if flag == true {
			res = res + len(words[i])
		}
	}
	return res
}

#
func countCharacters(words []string, chars string) int {
	res := 0
	for i := 0; i < len(words); i++ {
		flag := true
		for _, v := range words[i] {
			if strings.Count(words[i], string(v)) > strings.Count(chars, string(v)) {
				flag = false
				continue
			}
		}
		if flag == true {
			res = res + len(words[i])
		}
	}
	return res
}

#
func countCharacters(words []string, chars string) int {
	m := make([]int, 26)
	for i := range chars {
		m[chars[i]-'a']++
	}
	res := 0
	for i := 0; i < len(words); i++ {
		temp := make([]int, 26)
		flag := true
		for j := range words[i] {
			temp[words[i][j]-'a']++
		}
		if len(temp) > len(m) {
			continue
		}
		for k, v := range temp {
			if v > m[k] {
				flag = false
				break
			}
		}
		if flag == true {
			res = res + len(words[i])
		}
	}
	return res
}

1170.比较字符串最小字母出现频次(2)

  • 题目

我们来定义一个函数 f(s),其中传入参数 s 是一个非空字符串;
该函数的功能是统计 s  中(按字典序比较)最小字母的出现频次。
例如,若 s = "dcce",那么 f(s) = 2,因为最小的字母是 "c",它出现了 2 次。
现在,给你两个字符串数组待查表 queries 和词汇表 words,请你返回一个整数数组 answer 作为答案,
其中每个 answer[i] 是满足 f(queries[i]) < f(W) 的词的数目,W 是词汇表 words 中的词。
示例 1:输入:queries = ["cbd"], words = ["zaaaz"] 输出:[1]
解释:查询 f("cbd") = 1,而 f("zaaaz") = 3 所以 f("cbd") < f("zaaaz")。
示例 2:输入:queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"] 输出:[1,2]
解释:第一个查询 f("bbb") < f("aaaa"),第二个查询 f("aaa") 和 f("aaaa") 都 > f("cc")。
提示:
    1 <= queries.length <= 2000
    1 <= words.length <= 2000
    1 <= queries[i].length, words[i].length <= 10
    queries[i][j], words[i][j] 都是小写英文字母
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 数组辅助 O(n^2) O(n)
02 排序+二分查找 O(nlog(n)) O(n)
func numSmallerByFrequency(queries []string, words []string) []int {
	queriesArr := make([]int, len(queries))
	wordsArr := make([]int, len(words))
	res := make([]int, 0)
	for i := 0; i < len(words); i++ {
		wordsArr[i] = f(words[i])
	}
	for i := 0; i < len(queries); i++ {
		queriesArr[i] = f(queries[i])
		count := 0
		for j := 0; j < len(wordsArr); j++ {
			if queriesArr[i] < wordsArr[j] {
				count++
			}
		}
		res = append(res, count)
	}
	return res
}

func f(str string) int {
	min := str[0]
	count := 1
	for i := 1; i < len(str); i++ {
		if str[i] < min {
			min = str[i]
			count = 1
		} else if str[i] == min{
			count++
		}
	}
	return count
}

#
func numSmallerByFrequency(queries []string, words []string) []int {
	wordsArr := make([]int, len(words))
	res := make([]int, 0)
	for i := 0; i < len(words); i++ {
		wordsArr[i] = f(words[i])
	}
	sort.Ints(wordsArr)
	for i := 0; i < len(queries); i++ {
		value := f(queries[i])
		count := binarySearch(value, wordsArr)
		res = append(res, count)
	}
	return res
}

func binarySearch(value int, target []int) int {
	left := 0
	right := len(target) - 1
	for left < right {
		mid := left + (right-left)/2
		if target[mid] > value {
			right = mid
		} else {
			left = mid + 1
		}
	}
	if target[left] <= value {
		return 0
	}
	return len(target) - left
}

func f(str string) int {
	min := str[0]
	count := 1
	for i := 1; i < len(str); i++ {
		if str[i] < min {
			min = str[i]
			count = 1
		} else if str[i] == min {
			count++
		}
	}
	return count
}

1175.质数排列(1)

  • 题目

请你帮忙给从 1 到 n 的数设计排列方案,使得所有的「质数」都应该被放在「质数索引」(索引从 1 开始)上;
你需要返回可能的方案总数。
让我们一起来回顾一下「质数」:质数一定是大于 1 的,并且不能用两个小于它的正整数的乘积来表示。
由于答案可能会很大,所以请你返回答案 模 mod 10^9 + 7 之后的结果即可。
示例 1:输入:n = 5 输出:12
解释:举个例子,[1,2,5,4,3] 是一个有效的排列,但 [5,2,3,4,1] 不是,
因为在第二种情况里质数 5 被错误地放在索引为 1 的位置上。
示例 2:输入:n = 100 输出:682289015
提示:
    1 <= n <= 100
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历-全排列 O(n^3/2) O(1)
func numPrimeArrangements(n int) int {
	primeNum := 0
	for i := 2; i <= n; i++ {
		if isPrime(i) {
			primeNum++
		}
	}
	a := 1
	for i := 2; i <= primeNum; i++ {
		a = a * i % 1000000007
	}
	for i := 2; i <= n-primeNum; i++ {
		a = a * i % 1000000007
	}
	return a
}

func isPrime(n int) bool {
	if n == 2 || n == 3 {
		return true
	}
	for i := 2; i*i <= n; i++ {
		if n%i == 0 {
			return false
		}
	}
	return true
}

1184.公交站间的距离(2)

  • 题目

环形公交路线上有 n 个站,按次序从 0 到 n - 1 进行编号。
我们已知每一对相邻公交站之间的距离,
distance[i] 表示编号为 i 的车站和编号为 (i + 1) % n 的车站之间的距离。
环线上的公交车都可以按顺时针和逆时针的方向行驶。
返回乘客从出发点 start 到目的地 destination 之间的最短距离。
示例 1:输入:distance = [1,2,3,4], start = 0, destination = 1 输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。
示例 2:输入:distance = [1,2,3,4], start = 0, destination = 2 输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。
示例 3:输入:distance = [1,2,3,4], start = 0, destination = 3 输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。
提示:
    1 <= n <= 10^4
    distance.length == n
    0 <= start, destination < n
    0 <= distance[i] <= 10^4
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 遍历 O(n) O(1)
func distanceBetweenBusStops(distance []int, start int, destination int) int {
	x := 0
	y := 0
	for i := start; i != destination; i = (i + 1) % len(distance) {
		x = x + distance[i]
	}
	for i := destination; i != start; i = (i + 1) % len(distance) {
		y = y + distance[i]
	}
	if x > y {
		return y
	}
	return x
}

#
func distanceBetweenBusStops(distance []int, start int, destination int) int {
	x := 0
	sum := 0
	for i := 0; i < len(distance); i++ {
		sum = sum + distance[i]
		if start < destination {
			if i >= start && i < destination {
				x = x + distance[i]
			}
		} else {
			if i >= destination && i < start {
				x = x + distance[i]
			}
		}
	}
	if sum-x > x {
		return x
	}
	return sum - x
}

1185.一周中的第几天(3)

  • 题目

给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个 
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}。
示例 1:输入:day = 31, month = 8, year = 2019 输出:"Saturday"
示例 2:输入:day = 18, month = 7, year = 1999 输出:"Sunday"
示例 3:输入:day = 15, month = 8, year = 1993 输出:"Sunday"
提示:
    给出的日期一定是在 1971 到 2100 年之间的有效日期。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 内置函数 O(1) O(1)
02 公式 O(1) O(1)
03 遍历 O(1) O(1)
func dayOfTheWeek(day int, month int, year int) string {
	t, _ := time.Parse("2006-01-02", fmt.Sprintf("%04d-%02d-%02d", year, month, day))
	return t.Weekday().String()
}

#
// 蔡勒公式
// 基姆拉尔森计算公式
// https://baike.baidu.com/item/%E8%94%A1%E5%8B%92%E5%85%AC%E5%BC%8F
// https://www.cnblogs.com/SeekHit/p/7498408.html
// Week = (y+y/4-y/100+y/400+2*m+3*(m+1)/5+d) mod 7;
func dayOfTheWeek(day int, month int, year int) string {
	arr := []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
	if month == 1 || month == 2 {
		month = month + 12
		year--
	}
	week := (year + year/4 - year/100 + year/400 + 2*month + 3*(month+1)/5 + day) % 7
	return arr[week]
}

#
var arr = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
var monthDate = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

func dayOfTheWeek(day int, month int, year int) string {
	day1 := totalDay(1993, 8, 15)
	day2 := totalDay(year, month, day)
	diff := 6 - day1%7
	return arr[(day2+diff)%7]
}

func totalDay(year, month, day int) int {
	total := 0
	for i := 1971; i < year; i++ {
		total = total + 365
		if isLeap(i) {
			total = total + 1
		}
	}
	for i := 0; i < month-1; i++ {
		total = total + monthDate[i]
		if i == 1 && isLeap(year) {
			total = total + 1
		}
	}
	total = total + day
	return total
}

func isLeap(year int) bool {
	return year%400 == 0 || (year%4 == 0 && year%100 != 0)
}

1189.“气球”的最大数量(3)

  • 题目

给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 "balloon"(气球)。
字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 "balloon"。
示例 1:输入:text = "nlaebolko" 输出:1
示例 2:输入:text = "loonbalxballpoon" 输出:2
示例 3:输入:text = "leetcode" 输出:0
提示:
    1 <= text.length <= 10^4
    text 全部由小写英文字母组成
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历-数组辅助 O(n) O(1)
02 遍历-数组辅助 O(n) O(1)
03 内置函数 O(n) O(1)
func maxNumberOfBalloons(text string) int {
	m := make([]int, 26)
	str := "ablon"
	for i := 0; i < len(str); i++ {
		m[str[i]-'a']++
	}
	for i := 0; i < len(text); i++ {
		if m[text[i]-'a'] > 0 {
			m[text[i]-'a']++
		}
	}
	min := math.MaxInt32
	for k, v := range m {
		if v == 0 {
			continue
		}
		if k+'a' == 'l' || k+'a' == 'o' {
			v = (v - 1) / 2
		} else {
			v = v - 1
		}
		if v < min {
			min = v
		}
	}
	return min
}

#
func maxNumberOfBalloons(text string) int {
	m := make([]int, 26)
	for i := 0; i < len(text); i++ {
		m[text[i]-'a']++
	}
	res := 0
	str := "balloon"
	for {
		for i := 0; i < len(str); i++ {
			m[str[i]-'a']--
			if m[str[i]-'a'] < 0 {
				return res
			}
		}
		res++
	}
}

#
func maxNumberOfBalloons(text string) int {
	arr := make([]int, 0)
	str := "ablon"
	for i := 0; i < len(str); i++ {
		if str[i] == 'l' || str[i] == 'o' {
			arr = append(arr, strings.Count(text, string(str[i]))/2)
		} else {
			arr = append(arr, strings.Count(text, string(str[i])))
		}
	}
	min := arr[0]
	for i := 1; i < len(arr); i++ {
		if arr[i] < min {
			min = arr[i]
		}
	}
	return min
}

1200.最小绝对差(2)

  • 题目

给你个整数数组 arr,其中每个元素都 不相同。
请你找到所有具有最小绝对差的元素对,并且按升序的顺序返回。
示例 1:输入:arr = [4,2,1,3] 输出:[[1,2],[2,3],[3,4]]
示例 2:输入:arr = [1,3,6,10,15] 输出:[[1,3]]
示例 3:输入:arr = [3,8,-10,23,19,-4,-14,27] 输出:[[-14,-10],[19,23],[23,27]]
提示:
    2 <= arr.length <= 10^5
    -10^6 <= arr[i] <= 10^6
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 排序-遍历 O(nlog(n)) O(n)
02 排序-遍历 O(nlog(n)) O(n)
func minimumAbsDifference(arr []int) [][]int {
	sort.Ints(arr)
	result := make([][]int, 0)
	min := arr[1] - arr[0]
	result = append(result, []int{arr[0], arr[1]})
	for i := 2; i < len(arr); i++ {
		value := arr[i] - arr[i-1]
		if value < min {
			min = value
			result = make([][]int, 0)
			result = append(result, []int{arr[i-1], arr[i]})
		} else if value == min {
			result = append(result, []int{arr[i-1], arr[i]})
		}
	}
	return result
}

#
func minimumAbsDifference(arr []int) [][]int {
	sort.Ints(arr)
	result := make([][]int, 0)
	min := arr[1] - arr[0]
	for i := 2; i < len(arr); i++ {
		if min > arr[i]-arr[i-1] {
			min = arr[i] - arr[i-1]
		}
	}
	for i := 1; i < len(arr); i++ {
		if min == arr[i]-arr[i-1] {
			result = append(result, []int{arr[i-1], arr[i]})
		}
	}
	return result
}

1101-1200-Medium

1104.二叉树寻路(3)

  • 题目

在一棵无限的二叉树上,每个节点都有两个子节点,树中的节点 逐行 依次按“之” 字形进行标记。
如下图所示,在奇数行(即,第一行、第三行、第五行……)中,按从左到右的顺序进行标记;
而偶数行(即,第二行、第四行、第六行……)中,按从右到左的顺序进行标记。
给你树上某一个节点的标号 label,请你返回从根节点到该标号为 label 节点的路径,
该路径是由途经的节点标号所组成的。
示例 1:输入:label = 14 输出:[1,3,4,14]
示例 2:输入:label = 26 输出:[1,2,6,10,26]
提示:1 <= label <= 10^6
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历 O(log(n)) O(log(n))
02 遍历 O(log(n)) O(log(n))
03 位运算 O(log(n)) O(log(n))
func pathInZigZagTree(label int) []int {
	res := make([]int, 0)
	for label > 0 {
		res = append(res, label)
		label = label / 2
	}
	for i := 0; i < len(res)/2; i++ {
		res[i], res[len(res)-1-i] = res[len(res)-1-i], res[i]
	}
	i := 1
	if len(res)%2 == 0 {
		i = 2
	}
	for ; i < len(res); i = i + 2 {
		res[i] = (1<<i)*3 - 1 - res[i]
	}
	return res
}

# 2
func pathInZigZagTree(label int) []int {
	length := int(math.Log2(float64(label)))
	res := make([]int, length+1)
	res[length] = label
	length--
	i := 1
	for length >= 0 {
		target := int(math.Pow(2, float64(length+1))) + int(math.Pow(2, float64(length))) - 1
		if i%2 == 1 {
			res[length] = target - label/2
		} else {
			res[length] = label / 2
		}
		i++
		length--
		label = label / 2
	}
	return res
}

# 3
func pathInZigZagTree(label int) []int {
	res := make([]int, 0)
	for label > 1 {
		res = append([]int{label}, res...)
		label = label / 2
		length := bits.Len32(uint32(label)) - 1
		label = label ^ ((1 << length) - 1) // 7^3=4 => 111^11=100
	}
	res = append([]int{1}, res...)
	return res
}

1105.填充书架(1)

  • 题目

附近的家居城促销,你买回了一直心仪的可调节书架,打算把自己的书都整理到新的书架上。
你把要摆放的书 books都整理好,叠成一摞:
从上往下,第 i本书的厚度为 books[i][0],高度为 books[i][1]。
按顺序将这些书摆放到总宽度为shelf_width 的书架上。
先选几本书放在书架上(它们的厚度之和小于等于书架的宽度 shelf_width),然后再建一层书架。
重复这个过程,直到把所有的书都放在书架上。
需要注意的是,在上述过程的每个步骤中,摆放书的顺序与你整理好的顺序相同。 
例如,如果这里有 5 本书,那么可能的一种摆放情况是:
第一和第二本书放在第一层书架上,第三本书放在第二层书架上,第四和第五本书放在最后一层书架上。
每一层所摆放的书的最大高度就是这一层书架的层高,书架整体的高度为各层高之和。
以这种方式布置书架,返回书架整体可能的最小高度。
示例:输入:books = [[1,1],[2,3],[2,3],[1,1],[1,1],[1,1],[1,2]], shelf_width = 4 输出:6
解释:3 层书架的高度和为 1 + 3 + 2 = 6 。
第 2 本书不必放在第一层书架上。
提示:1 <= books.length <= 1000
1 <= books[i][0] <= shelf_width <= 1000
1 <= books[i][1] <= 1000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n^2) O(n)
func minHeightShelves(books [][]int, shelf_width int) int {
	n := len(books)
	dp := make([]int, n+1) // 以第i本书作为结尾的总高度
	for i := 1; i <= n; i++ {
		w, h := books[i-1][0], books[i-1][1]
		dp[i] = dp[i-1] + h // 当前这本书单独一层的高度
		for j := i - 1; j > 0; j-- {
			if w+books[j-1][0] <= shelf_width {
				w = w + books[j-1][0]
				h = max(h, books[j-1][1])
				dp[i] = min(dp[i], dp[j-1]+h)
			} else {
				break
			}
		}
	}
	return dp[n]
}

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

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

1109.航班预订统计(2)

  • 题目

这里有 n 个航班,它们分别从 1 到 n 进行编号。
我们这儿有一份航班预订表,
表中第 i 条预订记录 bookings[i] = [i, j, k] 意味着我们在从 i 到 j 的每个航班上预订了 k 个座位。
请你返回一个长度为 n 的数组 answer,按航班编号顺序返回每个航班上预订的座位数。
示例:输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5 输出:[10,55,45,25,25]
提示:1 <= bookings.length <= 20000
    1 <= bookings[i][0] <= bookings[i][1] <= n <= 20000
    1 <= bookings[i][2] <= 10000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 差分数组 O(n) O(n)
02 差分数组 O(n) O(n)
func corpFlightBookings(bookings [][]int, n int) []int {
	arr := make([]int, n+1)
	for i := 0; i < len(bookings); i++ {
		start := bookings[i][0] - 1
		end := bookings[i][1] - 1
		count := bookings[i][2]
		arr[start] = arr[start] + count
		arr[end+1] = arr[end+1] - count
	}
	res := make([]int, 0)
	total := 0
	for i := 0; i < n; i++ {
		total = total + arr[i]
		res = append(res, total)
	}
	return res
}

# 2
func corpFlightBookings(bookings [][]int, n int) []int {
	arr := make([]int, n)
	for i := 0; i < len(bookings); i++ {
		start := bookings[i][0]
		end := bookings[i][1]
		count := bookings[i][2]
		arr[start-1] = arr[start-1] + count
		if end < n {
			arr[end] = arr[end] - count
		}
	}
	for i := 1; i < n; i++ {
		arr[i] = arr[i] + arr[i-1]
	}
	return arr
}

1110.删点成林(1)

  • 题目

给出二叉树的根节点root,树上每个节点都有一个不同的值。
如果节点值在to_delete中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。
返回森林中的每棵树。你可以按任意顺序组织答案。
示例:输入:root = [1,2,3,4,5,6,7], to_delete = [3,5] 输出:[[1,2,null,4],[6],[7]]
提示:树中的节点数最大为1000。
每个节点都有一个介于1 到1000之间的值,且各不相同。
to_delete.length <= 1000
to_delete 包含一些从1 到1000、各不相同的值。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(n)
var res []*TreeNode
var m map[int]bool

func delNodes(root *TreeNode, to_delete []int) []*TreeNode {
	res = make([]*TreeNode, 0)
	m = make(map[int]bool)
	for i := 0; i < len(to_delete); i++ {
		m[to_delete[i]] = true
	}
	root = dfs(root)
	if root != nil {
		res = append(res, root)
	}
	return res
}

func dfs(root *TreeNode) *TreeNode {
	if root == nil {
		return nil
	}
	root.Left = dfs(root.Left)
	root.Right = dfs(root.Right)
	if m[root.Val] == true {
		if root.Left != nil {
			res = append(res, root.Left)
		}
		if root.Right != nil {
			res = append(res, root.Right)
		}
		return nil
	}
	return root
}

1111.有效括号的嵌套深度(3)

  • 题目

有效括号字符串 定义:对于每个左括号,都能找到与之对应的右括号,反之亦然。
详情参见题末「有效括号字符串」部分。
嵌套深度 depth 定义:即有效括号字符串嵌套的层数,depth(A) 表示有效括号字符串 A 的嵌套深度。
详情参见题末「嵌套深度」部分。
有效括号字符串类型与对应的嵌套深度计算方法如下图所示:
给你一个「有效括号字符串」 seq,请你将其分成两个不相交的有效括号字符串,A 和 B,
并使这两个字符串的深度最小。
    不相交:每个 seq[i] 只能分给 A 和 B 二者中的一个,不能既属于 A 也属于 B 。
    A 或 B 中的元素在原字符串中可以不连续。
    A.length + B.length = seq.length
    深度最小:max(depth(A), depth(B)) 的可能取值最小。 
划分方案用一个长度为 seq.length 的答案数组 answer 表示,编码规则如下:
    answer[i] = 0,seq[i] 分给 A 。
    answer[i] = 1,seq[i] 分给 B 。
如果存在多个满足要求的答案,只需返回其中任意 一个 即可。
示例 1:输入:seq = "(()())" 输出:[0,1,1,1,1,0]
示例 2:输入:seq = "()(())()" 输出:[0,0,0,1,1,0,1,1]
解释:本示例答案不唯一。
按此输出 A = "()()", B = "()()", max(depth(A), depth(B)) = 1,它们的深度最小。
像 [1,1,1,0,0,1,1,1],也是正确结果,其中 A = "()()()", B = "()", max(depth(A), depth(B)) = 1 。 
提示:1 < seq.size <= 10000
有效括号字符串:仅由 "(" 和 ")" 构成的字符串,对于每个左括号,都能找到与之对应的右括号,反之亦然。
下述几种情况同样属于有效括号字符串:
  1. 空字符串
  2. 连接,可以记作 AB(A 与 B 连接),其中 A 和 B 都是有效括号字符串
  3. 嵌套,可以记作 (A),其中 A 是有效括号字符串
嵌套深度:类似地,我们可以定义任意有效括号字符串 s 的 嵌套深度 depth(S):
  1. s 为空时,depth("") = 0
  2. s 为 A 与 B 连接时,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括号字符串
  3. s 为嵌套情况,depth("(" + A + ")") = 1 + depth(A),其中 A 是有效括号字符串
例如:"","()()",和 "()(()())" 都是有效括号字符串,
嵌套深度分别为 0,1,2,而 ")(" 和 "(()" 都不是有效括号字符串。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 统计 O(n) O(n)
02 找规律 O(n) O(n)
03 找规律 O(n) O(n)
func maxDepthAfterSplit(seq string) []int {
	res := make([]int, 0)
	level := 0
	for i := 0; i < len(seq); i++ {
		if seq[i] == '(' {
			level++
			res = append(res, level%2)
		} else {
			res = append(res, level%2)
			level--
		}
	}
	return res
}

# 2
func maxDepthAfterSplit(seq string) []int {
	res := make([]int, 0)
	for i := 0; i < len(seq); i++ {
		if seq[i] == '(' {
			res = append(res, i%2)
		} else {
			res = append(res, 1-i%2)
		}
	}
	return res
}

# 3
func maxDepthAfterSplit(seq string) []int {
	res := make([]int, 0)
	a, b := 0, 0
	for i := 0; i < len(seq); i++ {
		if seq[i] == '(' {
			// 谁少给谁
			if a <= b {
				a++
				res = append(res, 0)
			} else {
				b++
				res = append(res, 1)
			}
		} else {
			// 谁多减谁
			if a > b {
				a--
				res = append(res, 0)
			} else {
				b--
				res = append(res, 1)
			}
		}
	}
	return res
}

1123.最深叶节点的最近公共祖先(2)

  • 题目

给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。
回想一下:
叶节点 是二叉树中没有子节点的节点
树的根节点的深度为0,如果某一节点的深度为d,那它的子节点的深度就是d+1
如果我们假定 A 是一组节点S的 最近公共祖先,S中的每个节点都在以 A 为根节点的子树中,
且 A的深度达到此条件下可能的最大值。
注意:本题与力扣 865 重复:
示例 1:输入:root = [3,5,1,6,2,0,8,null,null,7,4] 输出:[2,7,4]
解释:我们返回值为 2 的节点,在图中用黄色标记。
在图中用蓝色标记的是树的最深的节点。
注意,节点 6、0 和 8 也是叶节点,但是它们的深度是 2 ,而节点 7 和 4 的深度是 3 。
示例 2:输入:root = [1] 输出:[1]
解释:根节点是树中最深的节点,它是它本身的最近公共祖先。
示例 3:输入:root = [0,1,3,null,2] 输出:[2]
解释:树中最深的叶节点是 2 ,最近公共祖先是它自己。
提示:给你的树中将有1 到 1000 个节点。
树中每个节点的值都在 1 到 1000 之间。
每个节点的值都是独一无二的。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 递归 O(n^2) O(log(n))
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
	res, _ := dfs(root, 0)
	return res
}

func dfs(root *TreeNode, level int) (*TreeNode, int) {
	if root == nil {
		return root, level
	}
	leftNode, left := dfs(root.Left, level+1)
	rightNode, right := dfs(root.Right, level+1)
	if left == right {
		return root, left + 1
	} else if left > right {
		return leftNode, left + 1
	}
	return rightNode, right + 1
}

# 2
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
	if root == nil{
		return nil
	}
	left := dfs(root.Left)
	right := dfs(root.Right)
	if left == right{
		return root
	}else if left > right{
		return lcaDeepestLeaves(root.Left)
	}
	return lcaDeepestLeaves(root.Right)
}

func dfs(root *TreeNode) (int) {
	if root == nil {
		return 0
	}
	left := dfs(root.Left)
	right := dfs(root.Right)
	return 1+max(left,right)
}

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

1124.表现良好的最长时间段(3)

  • 题目

给你一份工作时间表hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:输入:hours = [9,9,6,0,6,6,9] 输出:3
解释:最长的表现良好时间段是 [9,9,6]。
提示:1 <= hours.length <= 10000
0 <= hours[i] <= 16
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 单调栈+前缀和 O(n) O(n)
02 前缀和+暴力法 O(n^2) O(n)
03 哈希辅助 O(n) O(n)
func longestWPI(hours []int) int {
	arr := make([]int, 0)
	for i := 0; i < len(hours); i++ {
		if hours[i] > 8 {
			arr = append(arr, 1)
		} else {
			arr = append(arr, -1)
		}
	}
	temp := make([]int, len(hours)+1)
	for i := 1; i <= len(hours); i++ {
		temp[i] = temp[i-1] + arr[i-1]
	}
	stack := make([]int, 0)
	// 单调栈
	for i := 0; i < len(temp); i++ {
		if len(stack) == 0 || temp[stack[len(stack)-1]] > temp[i] {
			stack = append(stack, i)
		}
	}
	res := 0
	for i := len(temp) - 1; i >= 0; i-- {
		if len(stack) == 0 {
			break
		}
		for len(stack) > 0 && temp[i] > temp[stack[len(stack)-1]] {
			if i-stack[len(stack)-1] > res {
				res = i - stack[len(stack)-1]
			}
			stack = stack[:len(stack)-1]
		}
	}
	return res
}

# 2
func longestWPI(hours []int) int {
	arr := make([]int, 0)
	for i := 0; i < len(hours); i++ {
		if hours[i] > 8 {
			arr = append(arr, 1)
		} else {
			arr = append(arr, -1)
		}
	}
	temp := make([]int, len(hours)+1)
	for i := 1; i <= len(hours); i++ {
		temp[i] = temp[i-1] + arr[i-1]
	}
	res := 0
	for i := 0; i < len(hours); i++ {
		for j := i; j < len(hours); j++ {
			count := temp[j+1] - temp[i]
			if count > 0 {
				res = max(res, j-i+1)
			}
		}
	}
	return res
}

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

# 3
func longestWPI(hours []int) int {
	m := make(map[int]int)
	count := 0
	res := 0
	for i := 0; i < len(hours); i++ {
		if hours[i] > 8 {
			count++
		} else {
			count--
		}
		if count > 0 {
			res = i + 1
		} else {
			if _, ok := m[count]; ok == false {
				m[count] = i
			}
			// (count-(count-1))=1>0
			if _, ok := m[count-1]; ok == true {
				res = max(res, i-m[count-1])
			}

		}
	}
	return res
}

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

1129.颜色交替的最短路径(2)

  • 题目

在一个有向图中,节点分别标记为0, 1, ..., n-1。这个图中的每条边不是红色就是蓝色,且存在自环或平行边。
red_edges中的每一个[i, j]对表示从节点 i 到节点 j 的红色有向边。
类似地,blue_edges中的每一个[i, j]对表示从节点 i 到节点 j 的蓝色有向边。
返回长度为 n 的数组answer,其中answer[X]是从节点0到节点X的红色边和蓝色边交替出现的最短路径的长度。
如果不存在这样的路径,那么 answer[x] = -1。
示例 1:输入:n = 3, red_edges = [[0,1],[1,2]], blue_edges = [] 输出:[0,1,-1]
示例 2:输入:n = 3, red_edges = [[0,1]], blue_edges = [[2,1]] 输出:[0,1,-1]
示例 3:输入:n = 3, red_edges = [[1,0]], blue_edges = [[2,1]] 输出:[0,-1,-1]
示例 4:输入:n = 3, red_edges = [[0,1]], blue_edges = [[1,2]] 输出:[0,1,2]
示例 5:输入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]] 输出:[0,1,1]
提示:1 <= n <= 100
red_edges.length <= 400
blue_edges.length <= 400
red_edges[i].length == blue_edges[i].length == 2
0 <= red_edges[i][j], blue_edges[i][j] < n
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 广度优先搜索 O(n^2) O(n^2)
02 广度优先搜索 O(n^2) O(n^2)
var redArr [][]int
var blueArr [][]int
var res []int

func shortestAlternatingPaths(n int, red_edges [][]int, blue_edges [][]int) []int {
	redArr = make([][]int, n)
	blueArr = make([][]int, n)
	for i := 0; i < len(red_edges); i++ {
		a, b := red_edges[i][0], red_edges[i][1]
		redArr[a] = append(redArr[a], b)
	}
	for i := 0; i < len(blue_edges); i++ {
		a, b := blue_edges[i][0], blue_edges[i][1]
		blueArr[a] = append(blueArr[a], b)
	}
	res = make([]int, n)
	for i := 0; i < n; i++ {
		res[i] = -1
	}
	res[0] = 0
	bfs(n, 0) // 红
	bfs(n, 1) // 蓝
	return res
}

func bfs(n int, color int) {
	visited := make([][2]bool, n)
	visited[0][color] = true
	queue := make([]int, 0)
	queue = append(queue, 0)
	count := 0
	for len(queue) > 0 {
		length := len(queue)
		for i := 0; i < length; i++ {
			node := queue[i]
			targetColor := count % 2
			if targetColor == color { //偶数次和当前颜色一致
				for i := 0; i < len(redArr[node]); i++ {
					next := redArr[node][i]
					if visited[next][targetColor] == false {
						visited[next][targetColor] = true
						if res[next] == -1 || res[next] > count+1 {
							res[next] = count + 1
						}
						queue = append(queue, next)
					}
				}
			} else {
				for i := 0; i < len(blueArr[node]); i++ {
					next := blueArr[node][i]
					if visited[next][targetColor] == false {
						visited[next][targetColor] = true
						if res[next] == -1 || res[next] > count+1 {
							res[next] = count + 1
						}
						queue = append(queue, next)
					}
				}
			}
		}
		queue = queue[length:]
		count++
	}
}

# 2
func shortestAlternatingPaths(n int, red_edges [][]int, blue_edges [][]int) []int {
	redArr, blueArr := make([][]int, n), make([][]int, n)
	for i := 0; i < len(red_edges); i++ {
		a, b := red_edges[i][0], red_edges[i][1]
		redArr[a] = append(redArr[a], b)
	}
	for i := 0; i < len(blue_edges); i++ {
		a, b := blue_edges[i][0], blue_edges[i][1]
		blueArr[a] = append(blueArr[a], b)
	}
	res := make([]int, n) // res[0] = 0
	for i := 1; i < n; i++ {
		res[i] = -1
	}
	queue, visited := make([][2]int, 0), make([][2]bool, n)
	for i := 0; i < len(redArr[0]); i++ {
		queue = append(queue, [2]int{redArr[0][i], 0})
	}
	for i := 0; i < len(blueArr[0]); i++ {
		queue = append(queue, [2]int{blueArr[0][i], 1})
	}
	count := 1
	for len(queue) > 0 {
		length := len(queue)
		for i := 0; i < length; i++ {
			node, targetColor := queue[i][0], queue[i][1]
			if res[node] == -1 {
				res[node] = count
			}
			if targetColor == 0 && visited[node][targetColor] == false {
				visited[node][targetColor] = true
				for j := 0; j < len(blueArr[node]); j++ {
					queue = append(queue, [2]int{blueArr[node][j], 1})
				}
			}
			if targetColor == 1 && visited[node][targetColor] == false {
				visited[node][targetColor] = true
				for j := 0; j < len(redArr[node]); j++ {
					queue = append(queue, [2]int{redArr[node][j], 0})
				}
			}
		}
		queue = queue[length:]
		count++
	}
	return res
}

1130.叶值的最小代价生成树(3)

  • 题目

给你一个正整数数组arr,考虑所有满足以下条件的二叉树:
每个节点都有 0 个或是 2 个子节点。
数组arr中的值与树的中序遍历中每个叶节点的值一一对应。
(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。)
每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个32 位整数。
示例:输入:arr = [6,2,4] 输出:32
解释: 有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。
    24            24
   /  \          /  \
  12   4        6    8
 /  \               / \
6    2             2   4
提示:2 <= arr.length <= 40
1 <= arr[i] <= 15
答案保证是一个 32 位带符号整数,即小于2^31。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 单调栈 O(n) O(n)
02 动态规划 O(n^3) O(n^2)
03 单调栈 O(n) O(n)
func mctFromLeafValues(arr []int) int {
	res := 0
	stack := make([]int, 0) // 单调递减栈
	stack = append(stack, math.MaxInt32)
	for i := 0; i < len(arr); i++ {
		for len(stack) > 0 && arr[i] >= stack[len(stack)-1] { // 大于栈顶
			middle := stack[len(stack)-1] // 中间
			stack = stack[:len(stack)-1]
			left := stack[len(stack)-1]
			right := arr[i]
			res = res + middle*min(left, right)
		}
		stack = append(stack, arr[i])
	}
	for len(stack) > 2 {
		a := stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		b := stack[len(stack)-1]
		res = res + a*b
	}
	return res
}

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

# 2
func mctFromLeafValues(arr []int) int {
	n := len(arr)
	maxArr := make([][]int, n)
	for i := 0; i < n; i++ {
		maxArr[i] = make([]int, n)
		maxValue := arr[i]
		for j := i; j < n; j++ {
			maxValue = max(maxValue, arr[j])
			maxArr[i][j] = maxValue // i到j之间的最大值
		}
	}
	dp := make([][]int, n)
	for i := 0; i < n; i++ {
		dp[i] = make([]int, n)
	}
	for j := 0; j < n; j++ {
		for i := j - 1; i >= 0; i-- {
			dp[i][j] = math.MaxInt32
			for k := i; k+1 <= j; k++ {
				// dp[i][j]代表从i到j之间的最小代价
				dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]+maxArr[i][k]*maxArr[k+1][j])
			}
		}
	}
	return dp[0][n-1]
}

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

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

# 3
func mctFromLeafValues(arr []int) int {
	res := 0
	stack := make([]int, 0) // 单调递减栈
	for i := 0; i < len(arr); i++ {
		for len(stack) > 0 && arr[i] >= stack[len(stack)-1] { // 大于栈顶
			middle := stack[len(stack)-1] // 中间
			stack = stack[:len(stack)-1]
			right := arr[i]
			var left int
			if len(stack) == 0 {
				left = math.MaxInt32
			} else {
				left = stack[len(stack)-1]
			}
			res = res + middle*min(left, right)
		}
		stack = append(stack, arr[i])
	}
	for len(stack) >= 2 {
		a := stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		b := stack[len(stack)-1]
		res = res + a*b
	}
	return res
}

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

1131.绝对值表达式的最大值(2)

  • 题目

给你两个长度相等的整数数组,返回下面表达式的最大值:
|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|
其中下标 i,j 满足0 <= i, j < arr1.length。
示例 1:输入:arr1 = [1,2,3,4], arr2 = [-1,4,5,6] 输出:13
示例 2:输入:arr1 = [1,-2,-5,0,10], arr2 = [0,-2,-1,-7,-4] 输出:20
提示:2 <= arr1.length == arr2.length <= 40000
-10^6 <= arr1[i], arr2[i] <= 10^6
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 数学 O(n) O(n)
02 数学 O(n) O(1)
func maxAbsValExpr(arr1 []int, arr2 []int) int {
	/*
		|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|
		=  (arr1[i] + arr2[i] + i) - (arr1[j] + arr2[j] + j)
		=  (arr1[i] + arr2[i] - i) - (arr1[j] + arr2[j] - j)
		=  (arr1[i] - arr2[i] + i) - (arr1[j] - arr2[j] + j)
		=  (arr1[i] - arr2[i] - i) - (arr1[j] - arr2[j] - j)
		= -(arr1[i] + arr2[i] + i) + (arr1[j] + arr2[j] + j)
		= -(arr1[i] + arr2[i] - i) + (arr1[j] + arr2[j] - j)
		= -(arr1[i] - arr2[i] + i) + (arr1[j] - arr2[j] + j)
		= -(arr1[i] - arr2[i] - i) + (arr1[j] - arr2[j] - j)
		其中:
		A = arr1[i] + arr2[i] + i
		B = arr1[i] + arr2[i] - i
		C = arr1[i] - arr2[i] + i
		D = arr1[i] - arr2[i] - i
		结果:
		max( |arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|)
		= max(max(A) - min(A),max(B) - min(B),max(C) - min(C), max(D) - min(D))
	*/
	arr := make([][]int, 4)
	for i := 0; i < len(arr1); i++ {
		a, b := arr1[i], arr2[i]
		arr[0] = append(arr[0], a+b+i)
		arr[1] = append(arr[1], a+b-i)
		arr[2] = append(arr[2], a-b+i)
		arr[3] = append(arr[3], a-b-i)
	}
	a, b, c, d := getValue(arr[0]), getValue(arr[1]), getValue(arr[2]), getValue(arr[3])
	return max(a, max(b, max(c, d)))
}

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

func getValue(arr []int) int {
	minValue, maxValue := arr[0], arr[0]
	for i := 0; i < len(arr); i++ {
		if arr[i] > maxValue {
			maxValue = arr[i]
		}
		if arr[i] < minValue {
			minValue = arr[i]
		}
	}
	return maxValue - minValue
}

# 2
func maxAbsValExpr(arr1 []int, arr2 []int) int {
	aMaxValue, bMaxValue, cMaxValue, dMaxValue := math.MinInt32, math.MinInt32, math.MinInt32, math.MinInt32
	aMinValue, bMinValue, cMinValue, dMinValue := math.MaxInt32, math.MaxInt32, math.MaxInt32, math.MaxInt32
	for i := 0; i < len(arr1); i++ {
		aMaxValue = max(aMaxValue, arr1[i]+arr2[i]+i)
		aMinValue = min(aMinValue, arr1[i]+arr2[i]+i)
		bMaxValue = max(bMaxValue, arr1[i]+arr2[i]-i)
		bMinValue = min(bMinValue, arr1[i]+arr2[i]-i)
		cMaxValue = max(cMaxValue, arr1[i]-arr2[i]+i)
		cMinValue = min(cMinValue, arr1[i]-arr2[i]+i)
		dMaxValue = max(dMaxValue, arr1[i]-arr2[i]-i)
		dMinValue = min(dMinValue, arr1[i]-arr2[i]-i)
	}
	a, b := aMaxValue-aMinValue, bMaxValue-bMinValue
	c, d := cMaxValue-cMinValue, dMaxValue-dMinValue
	return max(a, max(b, max(c, d)))
}

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

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

1138.字母板上的路径(1)

  • 题目

我们从一块字母板上的位置(0, 0)出发,该坐标对应的字符为board[0][0]。
在本题里,字母板为board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"],如下所示。
我们可以按下面的指令规则行动:
如果方格存在,'U'意味着将我们的位置上移一行;
如果方格存在,'D'意味着将我们的位置下移一行;
如果方格存在,'L'意味着将我们的位置左移一列;
如果方格存在,'R'意味着将我们的位置右移一列;
'!'会把在我们当前位置 (r, c) 的字符board[r][c]添加到答案中。
(注意,字母板上只存在有字母的位置。)
返回指令序列,用最小的行动次数让答案和目标target相同。你可以返回任何达成目标的路径。
示例 1:输入:target = "leet" 输出:"DDR!UURRR!!DDD!"
示例 2:输入:target = "code" 输出:"RR!DDRR!UUL!R!"
提示:1 <= target.length <= 100
target仅含有小写英文字母。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 模拟 O(n) O(n)
func alphabetBoardPath(target string) string {
	res := ""
	x, y := 0, 0
	for i := 0; i < len(target); i++ {
		newX := int(target[i]-'a') / 5
		newY := int(target[i]-'a') % 5
		if x > newX {
			res = res + strings.Repeat("U", x-newX) // 优先向上:从Z往上
		}
		if y > newY {
			res = res + strings.Repeat("L", y-newY) // 优先向左:往Z走
		}
		if y < newY {
			res = res + strings.Repeat("R", newY-y) // 向右
		}
		if x < newX {
			res = res + strings.Repeat("D", newX-x) // 向下
		}
		res = res + "!"
		x, y = newX, newY
	}
	return res
}

1139.最大的以1为边界的正方形(1)

  • 题目

给你一个由若干 0 和 1 组成的二维网格grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。
如果不存在,则返回 0。
示例 1:输入:grid = [[1,1,1],[1,0,1],[1,1,1]] 输出:9
示例 2:输入:grid = [[1,1,0,0]] 输出:1
提示:1 <= grid.length <= 100
1 <= grid[0].length <= 100
grid[i][j] 为0或1
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 前缀和 O(n^3) O(n^2)
func largest1BorderedSquare(grid [][]int) int {
	res := 0
	arr := [100][100][2]int{}
	n, m := len(grid), len(grid[0])
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if grid[i][j] == 0 {
				continue
			}
			if i == 0 {
				arr[i][j][0] = 1
			} else {
				arr[i][j][0] = arr[i-1][j][0] + 1 // 当前坐标上边1的长度
			}
			if j == 0 {
				arr[i][j][1] = 1
			} else {
				arr[i][j][1] = arr[i][j-1][1] + 1 // 当前坐标左边1的长度
			}
			minValue := min(arr[i][j][0], arr[i][j][1]) // 左边、上边连续1选短的
			for k := minValue; k > res; k-- {
				if arr[i][j-k+1][0] >= k && arr[i-k+1][j][1] >= k { // 判断另外2条边
					res = k
					break
				}
			}
		}
	}
	return res * res
}

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

1140.石子游戏II(2)

  • 题目

亚历克斯和李继续他们的石子游戏。许多堆石子排成一行,每堆都有正整数颗石子piles[i]。
游戏以谁手中的石子最多来决出胜负。
亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。
在每个玩家的回合中,该玩家可以拿走剩下的前X堆的所有石子,其中1 <= X <= 2M。
然后,令M = max(M, X)。
游戏一直持续到所有石子都被拿走。
假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。
示例:输入:piles = [2,7,9,4,4] 输出:10
解释:如果亚历克斯在开始时拿走一堆石子,李拿走两堆,接着亚历克斯也拿走两堆。
在这种情况下,亚历克斯可以拿到 2 + 4 + 4 = 10 颗石子。 
如果亚历克斯在开始时拿走两堆石子,那么李就可以拿走剩下全部三堆石子。
在这种情况下,亚历克斯可以拿到 2 + 7 = 9 颗石子。
所以我们返回更大的 10。 
提示:1 <= piles.length <= 100
1 <= piles[i]<= 10 ^ 4
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n^3) O(n^2)
02 递归 O(n^2) O(n^2)
func stoneGameII(piles []int) int {
	n := len(piles)
	dp := make([][]int, n+1) // dp[i][j]=>有piles[i:],M=j的情况下得分
	for i := 0; i <= n; i++ {
		dp[i] = make([]int, n+1)
	}
	sum := 0
	for i := n - 1; i >= 0; i-- {
		sum = sum + piles[i]
		for M := 1; M <= n; M++ {
			if i+2*M >= n { // 可以全部拿走
				dp[i][M] = sum
			} else {
				for j := 1; j <= 2*M; j++ { // 尝试不同拿法,dp[i+j][max(j, M)]其中M=max(j,M)
					dp[i][M] = max(dp[i][M], sum-dp[i+j][max(j, M)])
				}
			}
		}
	}
	return dp[0][1]
}

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

# 2
var dp [][]int

func stoneGameII(piles []int) int {
	n := len(piles)
	dp = make([][]int, n+1)
	for i := 0; i <= n; i++ {
		dp[i] = make([]int, n+1)
	}
	for i := n - 2; i >= 0; i-- {
		piles[i] = piles[i] + piles[i+1]
	}
	return dfs(piles, 0, 1)
}

func dfs(piles []int, index, M int) int {
	if index >= len(piles) {
		return 0
	}
	if index+2*M >= len(piles) { // 可以全部拿走
		return piles[index]
	}
	if dp[index][M] > 0 {
		return dp[index][M]
	}
	res := 0
	for i := 1; i <= 2*M; i++ { // 尝试不同拿法
		res = max(res, piles[index]-dfs(piles, index+i, max(M, i)))
	}
	dp[index][M] = res
	return dp[index][M]
}

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

1143.最长公共子序列(3)

  • 题目

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:
它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
示例 1:输入:text1 = "abcde", text2 = "ace"  输出:3  
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:输入:text1 = "abc", text2 = "abc" 输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:输入:text1 = "abc", text2 = "def" 输出:0
解释:两个字符串没有公共子序列,返回 0。
提示:
    1 <= text1.length <= 1000
    1 <= text2.length <= 1000
    输入的字符串只含有小写英文字符。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划-二维 O(n^2) O(n^2)
02 动态规划-一维 O(n^2) O(n)
03 动态规划-一维 O(n^2) O(n)
func longestCommonSubsequence(text1 string, text2 string) int {
	n, m := len(text1), len(text2)
	dp := make([][]int, n+1)
	for i := 0; i < n+1; i++ {
		dp[i] = make([]int, m+1)
	}
	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			if text1[i-1] == text2[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else {
				dp[i][j] = max(dp[i][j-1], dp[i-1][j])
			}
		}
	}
	return dp[n][m]
}

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

# 2
func longestCommonSubsequence(text1 string, text2 string) int {
	n, m := len(text1), len(text2)
	prev := make([]int, m+1)
	cur := make([]int, m+1)
	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			if text1[i-1] == text2[j-1] {
				cur[j] = prev[j-1] + 1
			} else {
				cur[j] = max(prev[j], cur[j-1])
			}
		}
		copy(prev, cur)
	}
	return cur[m]
}

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

# 3
func longestCommonSubsequence(text1 string, text2 string) int {
	n, m := len(text1), len(text2)
	cur := make([]int, m+1)
	for i := 1; i <= n; i++ {
		pre := cur[0]
		for j := 1; j <= m; j++ {
			temp := cur[j]
			if text1[i-1] == text2[j-1] {
				cur[j] = pre + 1
			} else {
				cur[j] = max(cur[j], cur[j-1])
			}
			pre = temp
		}
	}
	return cur[m]
}

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

1144.递减元素使数组呈锯齿状(2)

  • 题目

给你一个整数数组nums,每次 操作会从中选择一个元素并 将该元素的值减少1。
如果符合下列情况之一,则数组A就是 锯齿数组:
每个偶数索引对应的元素都大于相邻的元素,即A[0] > A[1] < A[2] > A[3] < A[4] > ...
或者,每个奇数索引对应的元素都大于相邻的元素,即A[0] < A[1] > A[2] < A[3] > A[4] < ...
返回将数组nums转换为锯齿数组所需的最小操作次数。
示例 1:输入:nums = [1,2,3] 输出:2
解释:我们可以把 2 递减到 0,或把 3 递减到 1。
示例 2:输入:nums = [9,6,1,6,2] 输出:4
提示:1 <= nums.length <= 1000
1 <= nums[i] <= 1000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 遍历 O(n) O(1)
func movesToMakeZigzag(nums []int) int {
	n := len(nums)
	a, b := 0, 0
	for i := 0; i < n; i++ {
		if i%2 == 0 { // 偶数下标小,如果大于左右两边数,需要减去
			left, right := 0, 0
			if i > 0 && nums[i] >= nums[i-1] {
				left = nums[i] - nums[i-1] + 1
			}
			if i < n-1 && nums[i] >= nums[i+1] {
				right = nums[i] - nums[i+1] + 1
			}
			a = a + max(left, right)
		} else { // 奇数下标小,如果大于左右两边数,需要减去
			left, right := 0, 0
			if nums[i] >= nums[i-1] {
				left = nums[i] - nums[i-1] + 1
			}
			if i < n-1 && nums[i] >= nums[i+1] {
				right = nums[i] - nums[i+1] + 1
			}
			b = b + max(left, right)
		}
	}
	return min(a, b)
}

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

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

# 2
func movesToMakeZigzag(nums []int) int {
	n := len(nums)
	a, b := 0, 0
	for i := 0; i < n; i = i + 2 { // 偶数下标小,如果大于左右两边数,需要减去
		left, right := 0, 0
		if i > 0 && nums[i] >= nums[i-1] {
			left = nums[i] - nums[i-1] + 1
		}
		if i < n-1 && nums[i] >= nums[i+1] {
			right = nums[i] - nums[i+1] + 1
		}
		a = a + max(left, right)
	}
	for i := 1; i < n; i = i + 2 { // 奇数下标小,如果大于左右两边数,需要减去
		left, right := 0, 0
		if nums[i] >= nums[i-1] {
			left = nums[i] - nums[i-1] + 1
		}
		if i < n-1 && nums[i] >= nums[i+1] {
			right = nums[i] - nums[i+1] + 1
		}
		b = b + max(left, right)
	}
	return min(a, b)
}

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

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

1145.二叉树着色游戏(2)

  • 题目

有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中,
给出二叉树的根节点root,树上总共有 n 个节点,且 n 为奇数,其中每个节点上的值从1 到n各不相同。
游戏从「一号」玩家开始(「一号」玩家为红色,「二号」玩家为蓝色),最开始时,
「一号」玩家从 [1, n]中取一个值x(1 <= x <= n);
「二号」玩家也从[1, n]中取一个值y(1 <= y <= n)且y != x。
「一号」玩家给值为x的节点染上红色,而「二号」玩家给值为y的节点染上蓝色。
之后两位玩家轮流进行操作,每一回合,玩家选择一个他之前涂好颜色的节点,
将所选节点一个 未着色 的邻节点(即左右子节点、或父节点)进行染色。
如果当前玩家无法找到这样的节点来染色时,他的回合就会被跳过。
若两个玩家都没有可以染色的节点时,游戏结束。着色节点最多的那位玩家获得胜利 ✌️。
现在,假设你是「二号」玩家,根据所给出的输入,假如存在一个y值可以确保你赢得这场游戏,则返回true;
若无法获胜,就请返回 false。
示例:输入:root = [1,2,3,4,5,6,7,8,9,10,11], n = 11, x = 3 输出:True
解释:第二个玩家可以选择值为 2 的节点。
提示:二叉树的根节点为root,树上由 n 个节点,节点上的值从 1 到 n 各不相同。
n 为奇数。
1 <= x <= n<= 100
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 递归 O(n) O(log(n))
var targetNode *TreeNode

func btreeGameWinningMove(root *TreeNode, n int, x int) bool {
	dfsTarget(root, x)
	// 统计根节点、目标节点左子树、目标节点右子树
	return dfs(root) > n/2 || dfs(targetNode.Left) > n/2 || dfs(targetNode.Right) > n/2
}

func dfsTarget(root *TreeNode, target int) {
	if root != nil {
		if root.Val == target {
			targetNode = root
			return
		}
		dfsTarget(root.Left, target)
		dfsTarget(root.Right, target)
	}
}

func dfs(root *TreeNode) int {
	if root == nil || root == targetNode {
		return 0
	}
	return 1 + dfs(root.Left) + dfs(root.Right)
}

# 2
var leftSum, rightSum int

func btreeGameWinningMove(root *TreeNode, n int, x int) bool {
	leftSum = 0
	rightSum = 0
	total := dfs(root, x)
	return leftSum > n/2 || rightSum > n/2 || (total-1-leftSum-rightSum) > n/2
}

func dfs(root *TreeNode, x int) int {
	if root == nil {
		return 0
	}
	left := dfs(root.Left, x)
	right := dfs(root.Right, x)
	if root.Val == x {
		leftSum = left
		rightSum = right
	}
	return 1 + left + right
}

1146.快照数组(3)

  • 题目

实现支持下列接口的「快照数组」-SnapshotArray:
SnapshotArray(int length)- 初始化一个与指定长度相等的 类数组 的数据结构。初始时,每个元素都等于0。
void set(index, val)- 会将指定索引index处的元素设置为val。
int snap()- 获取该数组的快照,并返回快照的编号snap_id(快照号是调用snap()的总次数减去1)。
int get(index, snap_id)- 根据指定的snap_id选择快照,并返回该快照指定索引 index的值。
示例:输入:["SnapshotArray","set","snap","set","get"]
     [[3],[0,5],[],[0,6],[0,0]]
输出:[null,null,0,null,5]
解释:SnapshotArray snapshotArr = new SnapshotArray(3); // 初始化一个长度为 3 的快照数组
snapshotArr.set(0,5);  // 令 array[0] = 5
snapshotArr.snap();  // 获取快照,返回 snap_id = 0
snapshotArr.set(0,6);
snapshotArr.get(0,0);  // 获取 snap_id = 0 的快照中 array[0] 的值,返回 5
提示:1 <= length<= 50000
题目最多进行50000 次set,snap,和get的调用 。
0 <= index<length
0 <=snap_id <我们调用snap()的总次数
0 <=val <= 10^9
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 数组辅助 O(n) O(n^2)
02 哈希辅助 O(n) O(n^2)
03 数组辅助+二分查找 O(log(n)) O(n^2)
type SnapshotArray struct {
	id  int
	arr [][]Snap
}

type Snap struct {
	id    int
	value int
}

func Constructor(length int) SnapshotArray {
	return SnapshotArray{
		id:  0,
		arr: make([][]Snap, length),
	}
}

func (this *SnapshotArray) Set(index int, val int) {
	// 保存的是index的操作记录
	this.arr[index] = append(this.arr[index], Snap{
		id:    this.id,
		value: val,
	})
}

func (this *SnapshotArray) Snap() int {
	id := this.id
	this.id++
	return id
}

func (this *SnapshotArray) Get(index int, snap_id int) int {
	n := len(this.arr[index])
	i := 0
	// 根据index的历史记录,找到最后1个id为snap_id的记录
	// 其中id为snap_id的记录可能有多个
	for i < n && this.arr[index][i].id <= snap_id {
		i++
	}
	if i == 0 {
		return 0
	}
	i--
	return this.arr[index][i].value
}

# 2
type SnapshotArray struct {
	id  int
	arr []map[int]int
}

func Constructor(length int) SnapshotArray {
	return SnapshotArray{
		id:  0,
		arr: make([]map[int]int, length),
	}
}

func (this *SnapshotArray) Set(index int, val int) {
	if this.arr[index] == nil {
		this.arr[index] = make(map[int]int)
	}
	this.arr[index][this.id] = val
}

func (this *SnapshotArray) Snap() int {
	id := this.id
	this.id++
	return id
}

func (this *SnapshotArray) Get(index int, snap_id int) int {
	if this.arr[index] == nil {
		return 0
	}
	for ; snap_id >= 0; snap_id-- {
		if v, ok := this.arr[index][snap_id]; ok {
			return v
		}
	}
	return 0
}

# 3
type SnapshotArray struct {
	id  int
	arr [][]Snap
}

type Snap struct {
	id    int
	value int
}

func Constructor(length int) SnapshotArray {
	nums := make([][]Snap, length)
	for i := 0; i < length; i++ {
		nums[i] = []Snap{{ // 填充0
			id:    0,
			value: 0,
		}}
	}
	return SnapshotArray{
		id:  0,
		arr: nums,
	}
}

func (this *SnapshotArray) Set(index int, val int) {
	n := len(this.arr[index])
	if this.arr[index][n-1].id == this.id {
		this.arr[index][n-1].value = val
		return
	}
	this.arr[index] = append(this.arr[index], Snap{
		id:    this.id,
		value: val,
	})
}

func (this *SnapshotArray) Snap() int {
	id := this.id
	this.id++
	return id
}

func (this *SnapshotArray) Get(index int, snap_id int) int {
	n := len(this.arr[index])
	arr := this.arr[index]
	left, right := 0, n-1
	for left < right {
		mid := left + (right-left)/2
		if snap_id <= arr[mid].id {
			right = mid
		} else {
			left = mid + 1
		}
	}
	if left == n || arr[left].id > snap_id {
		return arr[left-1].value
	}
	return arr[left].value
}

1155.掷骰子的N种方法(2)

  • 题目

这里有d个一样的骰子,每个骰子上都有f个面,分别标号为1, 2, ..., f。
我们约定:掷骰子的得到总点数为各骰子面朝上的数字的总和。
如果需要掷出的总点数为target,请你计算出有多少种不同的组合情况(所有的组合情况总共有 f^d 种),
模10^9 + 7后返回。
示例 1:输入:d = 1, f = 6, target = 3 输出:1
示例 2:输入:d = 2, f = 6, target = 7 输出:6
示例 3:输入:d = 2, f = 5, target = 10 输出:1
示例 4:输入:d = 1, f = 2, target = 3 输出:0
示例 5:输入:d = 30, f = 30, target = 500 输出:222616187
提示:1 <= d, f <= 30
1 <= target <= 1000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n^3) O(n)
02 动态规划 O(n^3) O(n^2)
func numRollsToTarget(d int, f int, target int) int {
	dp := make([]int, target+1)
	dp[0] = 1
	for i := 0; i < d; i++ {
		next := make([]int, target+1)
		for j := 1; j <= f; j++ {
			for k := 0; k <= target-j; k++ {
				next[k+j] = (next[k+j] + dp[k]) % 1000000007
			}
		}
		dp = next
	}
	return dp[target]
}

# 2
func numRollsToTarget(d int, f int, target int) int {
	dp := make([][]int, d+1)
	for i := 0; i <= d; i++ {
		dp[i] = make([]int, target+1)
	}
	dp[0][0] = 1
	for i := 1; i <= d; i++ {
		for j := i; j <= target; j++ {
			for k := 1; k <= f; k++ {
				if j >= k {
					dp[i][j] = (dp[i][j] + dp[i-1][j-k]) % 1000000007
				}
			}
		}
	}
	return dp[d][target]
}

1156.单字符重复子串的最大长度(1)

  • 题目

如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。
给你一个字符串text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。
示例 1:输入:text = "ababa" 输出:3
示例 2:输入:text = "aaabaaa" 输出:6
示例 3:输入:text = "aaabbaaa" 输出:4
示例 4:输入:text = "aaaaa" 输出:5
示例 5:输入:text = "abcdef" 输出:1
提示:1 <= text.length <= 20000
text 仅由小写英文字母组成。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 滑动窗口 O(n) O(1)
func maxRepOpt1(text string) int {
	res := 0
	n := len(text)
	arr := [26]int{}
	for i := 0; i < n; i++ {
		v := int(text[i] - 'a')
		arr[v]++ // 统计每个字母出现的次数
	}
	for i := 0; i < n; {
		v := int(text[i] - 'a')
		countA := 0 // 统计相同的个数
		for i+countA < n && text[i] == text[i+countA] {
			countA++
		}
		j := i + countA + 1
		countB := 0 // 统计相隔1个不同字符往后连续的次数
		for j+countB < n && text[i] == text[j+countB] {
			countB++
		}
		// 2种情况
		// 1、a...axa...ay..a 可以拿1个来补齐:+1
		// 2、没有多余的补齐,可以拿第二段右侧最后一个点或者第一段左侧第一个点来补:+0
		total := min(countA+countB+1, arr[v]) // 取较少的
		res = max(res, total)                 // 更新结果
		i = i + countA                        // 窗口后移

	}
	return res
}

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

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

1161.最大层内元素和(2)

  • 题目

给你一个二叉树的根节点root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。
请你找出层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中最小 的那个。
示例 1:输入:root = [1,7,0,7,-8,null,null] 输出:2
解释:第 1 层各元素之和为 1,
第 2 层各元素之和为 7 + 0 = 7,
第 3 层各元素之和为 7 + -8 = -1,
所以我们返回第 2 层的层号,它的层内元素之和最大。
示例 2:输入:root = [989,null,10250,98693,-89388,null,null,null,-32127] 输出:2
提示:树中的节点数介于1和10^4之间
-10^5 <= node.val <= 10^5
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 层序遍历 O(n) O(n)
02 递归 O(n) O(n)
func maxLevelSum(root *TreeNode) int {
	res := 0
	maxValue := math.MinInt32
	if root == nil {
		return 0
	}
	queue := make([]*TreeNode, 0)
	queue = append(queue, root)
	level := 1
	for len(queue) > 0 {
		length := len(queue)
		sum := 0
		for i := 0; i < length; i++ {
			node := queue[i]
			sum = sum + node.Val
			if node.Left != nil {
				queue = append(queue, node.Left)
			}
			if node.Right != nil {
				queue = append(queue, node.Right)
			}
		}
		if sum > maxValue {
			maxValue = sum
			res = level
		}
		queue = queue[length:]
		level++
	}
	return res
}

# 2
var arr [][]int

func maxLevelSum(root *TreeNode) int {
	arr = make([][]int, 0)
	if root == nil {
		return 0
	}
	dfs(root, 0)
	res := 0
	maxValue := math.MinInt32
	for i := 0; i < len(arr); i++ {
		sum := 0
		for j := 0; j < len(arr[i]); j++ {
			sum = sum + arr[i][j]
		}
		if sum > maxValue {
			maxValue = sum
			res = i + 1
		}
	}
	return res
}

func dfs(root *TreeNode, level int) {
	if root == nil {
		return
	}
	if level == len(arr) {
		arr = append(arr, []int{})
	}
	arr[level] = append(arr[level], root.Val)
	dfs(root.Left, level+1)
	dfs(root.Right, level+1)
}

1162.地图分析(2)

  • 题目

你现在手里有一份大小为 N x N 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。
其中 0 代表海洋,1 代表陆地,请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):
(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。
如果网格上只有陆地或者海洋,请返回 -1。
示例 1:输入:[[1,0,1],[0,0,0],[1,0,1]] 输出:2
解释:海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 2。
示例 2:输入:[[1,0,0],[0,0,0],[0,0,0]] 输出:4
解释:  海洋单元格 (2, 2) 和所有陆地单元格之间的距离都达到最大,最大距离为 4。
提示: 1 <= grid.length == grid[0].length <= 100
    grid[i][j] 不是 0 就是 1。
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 广度优先搜索 O(n^2) O(n^2)
02 动态规划 O(n^2) O(n^2)
var dx = []int{-1, 1, 0, 0}
var dy = []int{0, 0, -1, 1}

func maxDistance(grid [][]int) int {
	queue := make([][2]int, 0)
	for i := 0; i < len(grid); i++ {
		for j := 0; j < len(grid[i]); j++ {
			if grid[i][j] == 1 {
				queue = append(queue, [2]int{i, j})
			}
		}
	}
	if len(queue) == 0 || len(queue) == len(grid)*len(grid[0]) {
		return -1
	}
	res := -1
	for len(queue) > 0 {
		res++
		length := len(queue)
		for i := 0; i < length; i++ {
			x1 := queue[i][0]
			y1 := queue[i][1]
			for i := 0; i < 4; i++ {
				x := x1 + dx[i]
				y := y1 + dy[i]
				if 0 <= x && x < len(grid) && 0 <= y && y < len(grid[0]) && grid[x][y] == 0 {
					queue = append(queue, [2]int{x, y})
					grid[x][y] = 2
				}
			}
		}
		queue = queue[length:]
	}
	return res
}

# 2
func maxDistance(grid [][]int) int {
	if len(grid) == 0 || len(grid[0]) == 0 {
		return -1
	}
	n := len(grid)
	m := len(grid[0])
	dp := make([][]int, n)
	for i := 0; i < n; i++ {
		dp[i] = make([]int, m)
	}
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if grid[i][j] == 1 {
				dp[i][j] = 0
			} else {
				dp[i][j] = math.MaxInt32
			}
		}
	}
	// 从上往下
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if grid[i][j] == 1 {
				continue
			}
			if i >= 1 {
				dp[i][j] = min(dp[i][j], dp[i-1][j]+1)
			}
			if j >= 1 {
				dp[i][j] = min(dp[i][j], dp[i][j-1]+1)
			}
		}
	}
	// 从下往上
	for i := n - 1; i >= 0; i-- {
		for j := m - 1; j >= 0; j-- {
			if grid[i][j] == 1 {
				continue
			}
			if i < n-1 {
				dp[i][j] = min(dp[i][j], dp[i+1][j]+1)
			}
			if j < m-1 {
				dp[i][j] = min(dp[i][j], dp[i][j+1]+1)
			}
		}
	}
	res := -1
	for i := 0; i < n; i++ {
		for j := 0; j < m; j++ {
			if grid[i][j] == 1 {
				continue
			}
			res = max(res, dp[i][j])
		}
	}
	if res == math.MaxInt32 {
		return -1
	}
	return res
}

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

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

1169.查询无效交易(2)

  • 题目

如果出现下述两种情况,交易 可能无效:
交易金额超过 ¥1000
或者,它和另一个城市中同名的另一笔交易相隔不超过 60 分钟(包含 60 分钟整)
每个交易字符串transactions[i]由一些用逗号分隔的值组成,这些值分别表示交易的名称,时间(以分钟计),金额以及城市。
给你一份交易清单transactions,返回可能无效的交易列表。你可以按任何顺序返回答案。
示例 1:输入:transactions = ["alice,20,800,mtv","alice,50,100,beijing"]
输出:["alice,20,800,mtv","alice,50,100,beijing"]
解释:第一笔交易是无效的,因为第二笔交易和它间隔不超过 60 分钟、名称相同且发生在不同的城市。同样,第二笔交易也是无效的。
示例 2:输入:transactions = ["alice,20,800,mtv","alice,50,1200,mtv"] 输出:["alice,50,1200,mtv"]
示例 3:输入:transactions = ["alice,20,800,mtv","bob,50,1200,mtv"] 输出:["bob,50,1200,mtv"]
提示:transactions.length <= 1000
每笔交易transactions[i]按"{name},{time},{amount},{city}"的格式进行记录
每个交易名称{name}和城市{city}都由小写英文字母组成,长度在1到10之间
每个交易时间{time}由一些数字组成,表示一个0到1000之间的整数
每笔交易金额{amount}由一些数字组成,表示一个0 到2000之间的整数
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n^3) O(n)
02 遍历 O(n^2) O(n)
type Node struct {
	Name   string
	Time   int
	Amount int
	City   string
	Index  int
	Flag   bool
}

func invalidTransactions(transactions []string) []string {
	res := make([]string, 0)
	m := make(map[string][]Node)
	n := len(transactions)
	for i := 0; i < n; i++ {
		arr := strings.Split(transactions[i], ",")
		name, city := arr[0], arr[3]
		t, _ := strconv.Atoi(arr[1])
		amount, _ := strconv.Atoi(arr[2])
		m[name] = append(m[name], Node{Name: name, Time: t,
			Amount: amount, City: city, Index: i,
		})
	}
	for _, v := range m {
		sort.Slice(v, func(i, j int) bool {
			return v[i].Time < v[j].Time
		})
		for i := 0; i < len(v); i++ {
			if v[i].Amount > 1000 {
				v[i].Flag = true
			}
			for j := i + 1; j < len(v); j++ {
				if v[j].Time-v[i].Time <= 60 {
					if v[i].City != v[j].City {
						v[i].Flag = true
						v[j].Flag = true
					}
				} else {
					break
				}
			}
		}
	}
	for _, v := range m {
		for i := 0; i < len(v); i++ {
			if v[i].Flag == true {
				res = append(res, transactions[v[i].Index])
			}
		}
	}
	return res
}

# 2
type Node struct {
	Name   string
	Time   int
	Amount int
	City   string
}

func invalidTransactions(transactions []string) []string {
	res := make([]string, 0)
	arr := make([]Node, 0)
	n := len(transactions)
	for i := 0; i < n; i++ {
		temp := strings.Split(transactions[i], ",")
		name, city := temp[0], temp[3]
		t, _ := strconv.Atoi(temp[1])
		amount, _ := strconv.Atoi(temp[2])
		arr = append(arr, Node{Name: name, Time: t, Amount: amount, City: city})
	}
	for i := 0; i < n; i++ {
		if arr[i].Amount > 1000 {
			res = append(res, transactions[i])
			continue
		}
		for j := 0; j < n; j++ {
			if i == j {
				continue
			}
			if arr[i].Name == arr[j].Name && arr[i].City != arr[j].City &&
				abs(arr[i].Time-arr[j].Time) <= 60 {
				res = append(res, transactions[i])
				break
			}
		}
	}
	return res
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

1171.从链表中删去总和值为零的连续节点(4)

  • 题目

给你一个链表的头节点head,请你编写代码,反复删去链表中由 总和值为 0 的连续节点组成的序列,
直到不存在这样的序列为止。
删除完毕后,请你返回最终结果链表的头节点。
你可以返回任何满足题目要求的答案。
(注意,下面示例中的所有序列,都是对ListNode对象序列化的表示。)
示例 1:输入:head = [1,2,-3,3,1] 输出:[3,1]
提示:答案 [1,2,1] 也是正确的。
示例 2:输入:head = [1,2,3,-3,4] 输出:[1,2,4]
示例 3:输入:head = [1,2,3,-3,-2] 输出:[1]
提示:给你的链表中可能有 1 到1000个节点。 
对于链表中的每个节点,节点的值:-1000 <= node.val <= 1000.
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
02 遍历 O(n) O(1)
03 哈希辅助 O(n) O(n)
04 递归 O(n^2) O(n)
func removeZeroSumSublists(head *ListNode) *ListNode {
	m := make(map[int]*ListNode)
	res := head
	sum := 0
	for cur := head; cur != nil; cur = cur.Next {
		sum = sum + cur.Val
		if sum == 0 { // 当前都为0
			res = cur.Next
			m = make(map[int]*ListNode)
			continue
		}
		if _, ok := m[sum]; ok == false {
			m[sum] = cur
			continue
		}
		// 出现重复
		first := m[sum]
		tempSum := sum
		for temp := first.Next; temp != cur; temp = temp.Next {
			tempSum = tempSum + temp.Val
			delete(m, tempSum)
		}
		first.Next = cur.Next
	}
	return res
}

# 2
func removeZeroSumSublists(head *ListNode) *ListNode {
	res := &ListNode{Next: head}
	for cur := res; cur != nil; {
		sum := 0
		for temp := cur.Next; temp != nil; temp = temp.Next {
			sum = sum + temp.Val
			if sum == 0 {
				cur.Next = temp.Next // 调整指针
			}
		}
		cur = cur.Next
	}
	return res.Next
}

# 3
func removeZeroSumSublists(head *ListNode) *ListNode {
	res := &ListNode{Next: head}
	m := make(map[int]*ListNode)
	sum := 0
	for cur := res; cur != nil; cur = cur.Next {
		sum = sum + cur.Val
		m[sum] = cur
	}
	sum = 0
	for cur := res; cur != nil; cur = cur.Next {
		sum = sum + cur.Val
		cur.Next = m[sum].Next
	}
	return res.Next
}

# 4
func removeZeroSumSublists(head *ListNode) *ListNode {
	if head == nil {
		return nil
	}
	sum := 0
	for cur := head; cur != nil; cur = cur.Next {
		sum = sum + cur.Val
		if sum == 0 {
			return removeZeroSumSublists(cur.Next)
		}
	}
	head.Next = removeZeroSumSublists(head.Next)
	return head
}

1177.构建回文串检测(1)

  • 题目

给你一个字符串s,请你对s的子串进行检测。
每次检测,待检子串都可以表示为queries[i] = [left, right, k]。
我们可以 重新排列 子串s[left], ..., s[right],并从中选择 最多 k项替换成任何小写英文字母。
如果在上述检测过程中,子串可以变成回文形式的字符串,那么检测结果为true,否则结果为false。
返回答案数组answer[],其中answer[i]是第i个待检子串queries[i]的检测结果。
注意:在替换时,子串中的每个字母都必须作为 独立的 项进行计数,
也就是说,如果s[left..right] = "aaa"且k = 2,我们只能替换其中的两个字母。
(另外,任何检测都不会修改原始字符串 s,可以认为每次检测都是独立的)
示例:输入:s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]] 输出:[true,false,false,true,true]
解释:queries[0] : 子串 = "d",回文。
queries[1] :子串 = "bc",不是回文。
queries[2] :子串 = "abcd",只替换 1 个字符是变不成回文串的。
queries[3] :子串 = "abcd",可以变成回文的 "abba"。 也可以变成 "baab",先重新排序变成 "bacd",然后把 "cd" 替换为 "ab"。
queries[4] :子串 = "abcda",可以变成回文的 "abcba"。
提示:1 <= s.length,queries.length<= 10^5
0 <= queries[i][0] <= queries[i][1] <s.length
0 <= queries[i][2] <= s.length
s 中只有小写英文字母
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 前缀和+位运算 O(n) O(n)
func canMakePaliQueries(s string, queries [][]int) []bool {
	n := len(s)
	arr := make([]int, n+1)
	cur := 0
	for i := 0; i < n; i++ {
		value := int(s[i] - 'a')
		cur = cur ^ (1 << value)
		arr[i+1] = cur
	}
	res := make([]bool, len(queries))
	for i := 0; i < len(queries); i++ {
		a, b, c := queries[i][0], queries[i][1], queries[i][2]
		status := arr[b+1] ^ arr[a]
		if bits.OnesCount(uint(status)) <= 2*c+1 { // 奇数次字母的个数,重新排列后最多允许2*c+1个
			res[i] = true
		}
	}
	return res
}

1186.删除一次得到子数组最大和(2)

  • 题目

给你一个整数数组,返回它的某个非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。
换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),
(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。
注意,删除一个元素后,子数组 不能为空。
请看示例:
示例 1:输入:arr = [1,-2,0,3] 输出:4
解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2,这样得到 [1, 0, 3],和最大。
示例 2:输入:arr = [1,-2,-2,3] 输出:3
解释:我们直接选出 [3],这就是最大和。
示例 3:输入:arr = [-1,-1,-1,-1] 输出:-1
解释:最后得到的子数组不能为空,所以我们不能选择 [-1] 并从中删去 -1 来得到 0。
     我们应该直接选择 [-1],或者选择 [-1, -1] 再从中删去一个 -1。
提示:1 <= arr.length <= 10^5
-10^4 <= arr[i] <= 10^4
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(n)
02 动态规划 O(n) O(1)
func maximumSum(arr []int) int {
	n := len(arr)
	dp := make([][2]int, n)
	dp[0][0] = arr[0] // 不删
	dp[0][1] = 0      // 删除
	res := dp[0][0]   // 长度为1,不删除
	for i := 1; i < n; i++ {
		dp[i][0] = max(dp[i-1][0]+arr[i], arr[i])
		dp[i][1] = max(dp[i-1][1]+arr[i], dp[i-1][0])
		res = max(res, max(dp[i][0], dp[i][1]))
	}
	return res
}

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

# 2
func maximumSum(arr []int) int {
	n := len(arr)
	a := arr[0] // 不删
	b := 0      // 删除
	res := a    // 长度为1,不删除
	for i := 1; i < n; i++ {
		a, b = max(a+arr[i], arr[i]), max(b+arr[i], a)
		res = max(res, max(a, b))
	}
	return res
}

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

1190.反转每对括号间的子串(2)

  • 题目

给出一个字符串s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
示例 1:输入:s = "(abcd)" 输出:"dcba"
示例 2:输入:s = "(u(love)i)" 输出:"iloveu"
示例 3:输入:s = "(ed(et(oc))el)" 输出:"leetcode"
示例 4:输入:s = "a(bcdefghijkl(mno)p)q" 输出:"apmnolkjihgfedcbq"
提示:0 <= s.length <= 2000
s 中只有小写英文字母和括号
我们确保所有括号都是成对出现的
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 O(n^2) O(n)
02 O(n) O(n)
func reverseParentheses(s string) string {
	arr := []byte(s)
	stack := make([]int, 0)
	for i := 0; i < len(s); i++ {
		if s[i] == '(' {
			stack = append(stack, i)
		} else if s[i] == ')' {
			reverse(arr, stack[len(stack)-1], i)
			stack = stack[:len(stack)-1]
		}
	}
	s = string(arr)
	s = strings.ReplaceAll(s, "(", "")
	s = strings.ReplaceAll(s, ")", "")
	return s
}

func reverse(arr []byte, i, j int) {
	for i < j {
		arr[i], arr[j] = arr[j], arr[i]
		i++
		j--
	}
}

# 2
func reverseParentheses(s string) string {
	stack := make([]int, 0)
	m := make(map[int]int)
	for i := 0; i < len(s); i++ {
		if s[i] == '(' {
			stack = append(stack, i)
		} else if s[i] == ')' {
			last := stack[len(stack)-1]
			m[i] = last
			m[last] = i
			stack = stack[:len(stack)-1]
		}
	}
	dir := 1
	res := make([]byte, 0)
	for i := 0; i < len(s); i = i + dir {
		if s[i] == '(' || s[i] == ')' {
			i = m[i]   // 从反方向遍历
			dir = -dir // 变换方向,+1/-1
		} else {
			res = append(res, s[i])
		}
	}
	return string(res)
}

1191.K次串联后最大子数组之和(1)

  • 题目

给你一个整数数组 arr 和一个整数 k。
首先,我们要对该数组进行修改,即把原数组 arr 重复 k 次。
    举个例子,如果 arr = [1, 2] 且 k = 3,那么修改后的数组就是 [1, 2, 1, 2, 1, 2]。
然后,请你返回修改后的数组中的最大的子数组之和。
注意,子数组长度可以是 0,在这种情况下它的总和也是 0。
由于 结果可能会很大,所以需要 模(mod) 10^9 + 7 后再返回。 
示例 1:输入:arr = [1,2], k = 3 输出:9
示例 2:输入:arr = [1,-2,1], k = 5 输出:2
示例 3:输入:arr = [-1,-2], k = 7 输出:0
提示:
    1 <= arr.length <= 10^5
    1 <= k <= 10^5
    -10^4 <= arr[i] <= 10^4
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
func kConcatenationMaxSum(arr []int, k int) int {
	cur := 0
	sum := 0
	res := 0
	for i := 0; i < len(arr); i++ {
		sum = sum + arr[i]
		cur = max(cur+arr[i], arr[i])
		res = max(res, cur)
	}
	for i := 0; i < len(arr); i++ {
		cur = max(cur+arr[i], arr[i])
		res = max(res, cur)
	}
	if sum <= 0 {
		return res % 1000000007
	}
	return (res + (k-2)*sum) % 1000000007
}

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

1101-1200-Hard

1147.段式回文(2)

  • 题目

段式回文 其实与 一般回文 类似,只不过是最小的单位是 一段字符而不是 单个字母。
举个例子,对于一般回文 "abcba" 是回文,而 "volvo" 不是,但如果我们把"volvo" 分为 "vo"、"l"、"vo" 三段,
则可以认为 “(vo)(l)(vo)” 是段式回文(分为 3 段)。
给你一个字符串text,在确保它满足段式回文的前提下,请你返回 段 的最大数量k。
如果段的最大数量为k,那么存在满足以下条件的a_1, a_2, ..., a_k:
每个a_i都是一个非空字符串;
将这些字符串首位相连的结果a_1 + a_2 + ... + a_k和原始字符串text相同;
对于所有1 <= i <= k,都有a_i = a_{k+1 - i}。
示例 1:输入:text = "ghiabcdefhelloadamhelloabcdefghi" 输出:7
解释:我们可以把字符串拆分成 "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)"。
示例 2:输入:text = "merchant" 输出:1
解释:我们可以把字符串拆分成 "(merchant)"。
示例 3:输入:text = "antaprezatepzapreanta" 输出:11
解释:我们可以把字符串拆分成 "(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)"。
示例 4:输入:text = "aaa" 输出:3
解释:我们可以把字符串拆分成 "(a)(a)(a)"。
提示:text仅由小写英文字符组成。
1 <= text.length <= 1000
  • 解题思路

No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(n)
02 双指针 O(n) O(1)
func longestDecomposition(text string) int {
	n := len(text)
	if n <= 1 {
		return n
	}
	for i := 1; i <= n/2; i++ {
		if text[:i] == text[n-i:] {
			return 2 + longestDecomposition(text[i:n-i])
		}
	}
	return 1
}

# 2
func longestDecomposition(text string) int {
	n := len(text)
	if n <= 1 {
		return n
	}
	res := 0
	left, right := 0, n
	for left < right {
		for k := 1; ; k++ {
			if k > (right-left)/2 { // 长度超过剩下的1/2
				res++
				return res
			}
			if text[left:left+k] == text[right-k:right] {
				res = res + 2 // 可以切割,长度+2
				left = left + k
				right = right - k
				break
			}
		}
	}
	return res
}

1187.使数组严格递增

题目

给你两个整数数组arr1 和 arr2,返回使arr1严格递增所需要的最小「操作」数(可能为 0)。
每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,
分别为i 和j,0 <=i < arr1.length和0 <= j < arr2.length,然后进行赋值运算arr1[i] = arr2[j]。
如果无法让arr1严格递增,请返回-1。
示例 1:输入:arr1 = [1,5,3,6,7], arr2 = [1,3,2,4] 输出:1
解释:用 2 来替换 5,之后 arr1 = [1, 2, 3, 6, 7]。
示例 2:输入:arr1 = [1,5,3,6,7], arr2 = [4,3,1] 输出:2
解释:用 3 来替换 5,然后用 4 来替换 3,得到 arr1 = [1, 3, 4, 6, 7]。
示例3:输入:arr1 = [1,5,3,6,7], arr2 = [1,6,3,3] 输出:-1
解释:无法使 arr1 严格递增。
提示:1 <= arr1.length, arr2.length <= 2000
0 <= arr1[i], arr2[i] <= 10^9

解题思路

No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(n)