From cebaf32ea7a734399962fb6eb7b6038749057744 Mon Sep 17 00:00:00 2001 From: gameloader Date: Sun, 26 May 2024 12:36:25 +0800 Subject: [PATCH] leetcode update --- content/posts/leetcode.md | 387 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 80691fb..9369462 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -6054,3 +6054,390 @@ func maximumValueSum(nums []int, k int, edges [][]int) int64 { } ``` + +## day83 2024-05-20 + +### 1863. Sum of All Subset XOR Totals + +The XOR total of an array is defined as the bitwise XOR of all its elements, or 0 if the array is empty. + +For example, the XOR total of the array [2,5,6] is 2 XOR 5 XOR 6 = 1. +Given an array nums, return the sum of all XOR totals for every subset of nums. + +Note: Subsets with the same elements should be counted multiple times. + +An array a is a subset of an array b if a can be obtained from b by deleting some (possibly zero) elements of b. + +![0520iz2pn7M78pYk](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0520iz2pn7M78pYk.png) + +### 题解 + +本题如何求出所有组合并且保存以前计算过的组合的状态是关键, 考虑只有两个数的情况, 一共有三个组合, 1,2,1-2. 在此基础上考虑三个数的情况,会发现共有7种组合, 1,2,1-2,3,1-3,2-3,1-2-3. 可以注意到, 除了3自己这种情况外, 其余包含3的情况是将3和只有两个数的所有情况组合. 因此可以得出规律, 对于前n+1个数, 可以将包含前n个数的所有组合与第n+1个数组合, 再加上第n+1个数自身即为前n+1个数的所有组合. 用数组保存到当前位置的所有已经计算过的组合即可. + +### 代码 + +```go +func subsetXORSum(nums []int) int { + save_state := []int{} + result := 0 + for _, value := range nums{ + newstate := 0 + for _, saved := range save_state{ + newstate = value ^ saved + result += newstate + save_state = append(save_state, newstate) + } + result += value + save_state = append(save_state, value) + } + return result +} +``` + +## day84 2024-05-21 + +### 78. Subsets + +Given an integer array nums of unique elements, return all possible +subsets +(the power set). + +The solution set must not contain duplicate subsets. Return the solution in any order. +![0521re4BxttZ1GeL](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0521re4BxttZ1GeL.png) + +### 题解 + +本题在寻找全部的子数组的方法上和昨天题目的方法一致, 都是保存前n个数能构成的所有组合的集合,再与第n+1个数依次组合(空也算单独一个组合), 就得到了前n个数能构成的所有组合. + +### 代码 + +```go +func subsets(nums []int) [][]int { + result := [][]int{} + result = append(result, []int{}) + for _, value := range nums{ + for _, subset := range result{ + newset := []int{} + newset = append(newset, subset...) + newset = append(newset, value) + result = append(result, newset) + } + } + return result +} +``` + +### 总结 + +还有一种通过dfs递归实现的方法也很有趣, 核心在于把握所有组合中原来数组中的数要么出现要么不出现, 那么就可以递归调用每次选择包含或者不包含下一个数, 通过将所有的包含与不包含组合, 就得到了所有的组合. + +```go +var res [][]int + +func subsets(nums []int) [][]int { + res = nil + if len(nums) == 0 { + return res + } + + var subset []int + dfs(subset, nums) + return res +} + +func dfs(subset, nums []int) { + if len(nums) == 0 { + curr := make([]int, len(subset)) + copy(curr, subset) + res = append(res, curr) + + return + } + + //include + included := append(subset, nums[0]) + dfs(included, nums[1:]) + + //not include + dfs(subset, nums[1:]) +} + + +``` + +## day85 2024-05-22 + +### 131. Palindrome Partitioning + +Given a string s, partition s such that every +substring +of the partition is a +palindrome +. Return all possible palindrome partitioning of s. + +![0522DOnpFcYXV1k3](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0522DOnpFcYXV1k3.png) + +### 题解 + +本题需要枚举所有的子集分割并判断分割后的这些子集是否均为回文串. 这里可以使用回溯算法. 可以进行一些剪枝, 因为题目限制了所有字串都必须为回文串, 因此若产生的某个子串已经不是回文串则可以直接停止遍历其之后的串. 回溯算法可以使用递归实现. 本质上是一种深度优先搜索. + +### 代码 + +```go +func partition(s string) [][]string { + result := [][]string{} + if len(s) == 1{ + return [][]string{[]string{s}} + } + for index, _ := range s[0:len(s)-1]{ + head := s[0:index+1] + newtails := [][]string{} + if ispalin(head){ + newtails = partition(s[index+1:]) + for _, tail := range newtails{ + newresult := []string{head} + newresult = append(newresult, tail...) + result = append(result, newresult) + } + } + } + if ispalin(s){ + result = append(result, []string{s}) + } + return result +} + +func ispalin(s string) bool{ + begin := 0 + tail := len(s) - 1 + for begin <= tail{ + if s[begin] != s[tail]{ + return false + } + begin++ + tail-- + } + return true +} +``` + +## day86 2024-05-23 + +### 2597. The Number of Beautiful Subsets + +You are given an array nums of positive integers and a positive integer k. + +A subset of nums is beautiful if it does not contain two integers with an absolute difference equal to k. + +Return the number of non-empty beautiful subsets of the array nums. + +A subset of nums is an array that can be obtained by deleting some (possibly none) elements from nums. Two subsets are different if and only if the chosen indices to delete are different. + +![0523SGYugmpe3CLf](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0523SGYugmpe3CLf.png) + +### 题解 + +本题和求数组的全部子集有一些相似之处, 先排序, 排序可以使得在找与当前数字绝对值相差k的数时只需要看前面的数即可. 保存到当前位置处得到的全部合格的子数组(漂亮的). 在继续遍历时将新的数与之前的所有合格子数组比较, 看原来的子数组中是否有与新的数冲突的数. 若有则不能与新的数组合, 没有则可以组合. 这里为了快速寻找是否冲突, 可以用数组来保存之前的合格子数组, 将之前合格子数组中所有的数作为数组的下标.下标对应位置的值为1. 不包含的数的下标对应位置为0. 考虑数组中的数最大为1000. 这种方法是可以保存所有数的情况的. + +### 代码 + +```go +func beautifulSubsets(nums []int, k int) int { + sort.Ints(nums) + result := 0 + subsets := [][]int{} + new := make([]int,32) + subsets = append(subsets, new) + + for _, value := range nums{ + if value <= k{ + for _, set := range subsets{ + newset := []int{} + newset = append(newset, set...) + newset[value/32] = newset[value/32] | 1 << (value % 32) + subsets = append(subsets, newset) + result++ + } + }else{ + for _,set := range subsets{ + if (set[(value-k)/32] & (1 << ((value-k) % 32))) == 0{ + newset := []int{} + newset = append(newset, set...) + newset[value/32] = newset[value/32] | 1 << (value % 32) + subsets = append(subsets, newset) + result++ + } + } + } + } + return result +} + +``` + +### 总结 + +该方法时间复杂度比较高, 可参见下面的题解中讲解的动态规划解法, 这里要思考为什么同余的性质如此重要. 因为同余将数组中的数按照不同的性质分为了不同的组, 不同的组的数一定不会相差k因此可以随意组合. 最终结果相当于在这些组中任意选取组内一个可行组合进行组间组合. 这样通过将所有组的组内可行方案相乘就能得到最终结果. 而在组内, 通过排序, 因此各个数都同余, 只有相邻的数可能相差k, 不相邻的相差一定是k的2倍及以上, 这样就能用动态规划来求得组内的全部方案, 可以把问题转换为子问题, 如果不相邻的数也可能相差k, 那么问题就很难用动态规划来解决, 因为不知道什么时候才能解决一个子问题, 把相差k限制在相邻数, 我们就知道只要经过了这个相邻的数, 前面的数对后面一定没有影响了, 就解决了一个子问题, 这样相当于隐含携带了前面的数的信息. + + +## day87 2024-05-24 + +### 1255. Maximum Score Words Formed by Letters + +Given a list of words, list of single letters (might be repeating) and score of every character. + +Return the maximum score of any valid set of words formed by using the given letters (words[i] cannot be used two or more times). + +It is not necessary to use all characters in letters and each letter can only be used once. Score of letters 'a', 'b', 'c', ... ,'z' is given by score[0], score[1], ... , score[25] respectively. + +![0524pJqWG7GqQaky](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0524pJqWG7GqQaky.png) + +### 题解 + +本题使用回溯算法, 遍历所有可能的组合并剪枝即可. 首先扫描保存所有可用的字母的个数. 循环当前的words数组, 判断当前词是否能由可用字母组成并计算score, 不能组成则跳到下一个词, 计算完成当前词的score后递归调用计算后面的词组成的词数组用剩余的可用字母能得到的最大值. 将其与当前词的score加和并更新全局最大值, 继续遍历下一个词. 注意这里的递归可能有重复状态, 但大部分是不重复的, 如第一个词用掉了两个a, 后面的词是在可用字母少了两个a的情况下的最大值. 这样求得的是包含第一个词的情况下能得到的最大值. 继续向后遍历, 此时求的是不包含第一个词的情况下的最大值, 意味着虽然也是求除第一个词之外数组中所有词能组成的最大值, 但此时能用全部可用字母, 刚才则是求少了两个a的情况, 因此得到的结果未必相同. 因为这两个a的影响我们不能预先知道, 所以不能直接记忆化上一次的结果拿来用. 可能的优化就是在剩余可用字母完全相同的情况下求包含相同的词的数组能取得的最大值可以记忆化. + +### 代码 + +```go +func maxScoreWords(words []string, letters []byte, score []int) int { + num := make([]int, 26) + for _, char := range letters{ + num[char - 'a']++ + } + maxscore := maxscoreword(words, num, score) + return maxscore + +} + +func maxscoreword(words []string, num []int, score []int) int{ + maxscore := 0 + for index, word := range words{ + tempnum := []int{} + tempnum = append(tempnum, num...) + tempmax := 0 + flag := 0 + for _, char := range word{ + bytechar := byte(char) + if tempnum[bytechar - 'a'] <= 0{ + flag = 1 + break + }else{ + tempnum[bytechar - 'a']-- + if tempnum[bytechar - 'a'] < 0{ + flag = 1 + break + }else{ + tempmax += score[bytechar - 'a'] + } + } + } + if flag == 1{ + continue + }else{ + tempmax += maxscoreword(words[index+1:], tempnum, score ) + maxscore = max(maxscore, tempmax) + } + } + return maxscore +} +``` + +## day89 2024-05-25 + +### 140. Word Break II + +Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences in any order. + +Note that the same word in the dictionary may be reused multiple times in the segmentation. +![0525rcv0Aguf2aYM](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0525rcv0Aguf2aYM.png) + +### 题解 + +为了快速检索分割出来的某个单词是否在词典中, 要先将词典数组转换为哈希表. 还可以注意到, 从原始字符串的某个下标开始能得到的所有可行分割的词的组合是固定的. 则可以扫描字符串, 分割出一个有效单词, 再对剩余字符串调用递归函数获取所有剩余字符串的可行分割, 在递归获取当前串的所有可行分割的过程中将当前下标对应的所有可行分割保存到全局记忆数组中. 后续若再次遇到从该下标开始分割, 直接从记忆数组中获取以该下标开始的所有可行分割串返回即可. + +### 代码 + +```go +func wordBreak(s string, wordDict []string) []string { + words := map[string]bool{} + for _, word := range wordDict{ + words[word] = true + } + memorybreak := make([][]string, 21) + result := breaks(s, words, memorybreak, 0) + return result +} + +func breaks(s string, words map[string]bool, memorybreak [][]string, pointer int)[]string{ + if len(memorybreak[pointer]) > 0{ + return memorybreak[pointer] + }else{ + result := []string{} + for index,_ := range s{ + if words[s[:index+1]] == true{ + if index == len(s)-1{ + result = append(result, s) + return result + } + tails := breaks(s[index+1:], words, memorybreak, pointer+index+1) + if len(tails) <= 0{ + continue + } + for _,tailstring := range tails{ + head := "" + head = head + s[:index+1] + " " + tailstring + result = append(result, head) + } + } + } + memorybreak[pointer] = result + return result + } +} +``` + +## day90 2024-05-26 + +### 552. Student Attendance Record II + +An attendance record for a student can be represented as a string where each character signifies whether the student was absent, late, or present on that day. The record only contains the following three characters: + +'A': Absent. +'L': Late. +'P': Present. +Any student is eligible for an attendance award if they meet both of the following criteria: + +The student was absent ('A') for strictly fewer than 2 days total. +The student was never late ('L') for 3 or more consecutive days. +Given an integer n, return the number of possible attendance records of length n that make a student eligible for an attendance award. The answer may be very large, so return it modulo 109 + 7. + +![0526mSztEswHh8kF](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0526mSztEswHh8kF.png) + +### 题解 + +本题为一道难题, 一看有不同的状态, 状态还受到一些限制, 显然首先想到动态规划. 关键在于如何从子问题到当前问题. 首先A只能出现一次, 所以可以通过A0和A1表示对A是否出现这一状态的记忆. L不能连续出现三次, 则L出现可以用L1和L2表示L连续出现1次和2次. P表示正常的出勤. 那么全部状态可以表示成如下图所示的6种(图中下标b表示前一天). 接下来考虑对于每一种情况(A,L,P)其出现时与前一天这6种状态中的哪些有关. 可以看到对于出现L如果当前L连续出现了两次并且A从未出现过, 那么这种状态只有在前一天也为L并且A从未出现过才会发生. 如果L出现了一次, 那么在这之前的状态肯定是非L的即为P, A从未出现过, 则状态数与A0P相同. 对于A0P来说, 其表示当前采用的字母为P, A从未出现, 则无论前一天状态是P还是L已经连续出现了1次或2次今天的状态都可以选择P. 这样将今天的状态与前一天的状态的关系对应起来即可得到只与前一天状态有关的状态转移方式. 实现这一状态转移过程即可. 注意在转移的过程中要及时的将存在加和的转移过程模10^9+7因为数字很快就会变得很大, 而中间模与最后模结果相同. 这样可以避免计算过程的数字溢出. + +![05265BDDZlIMG_7DF7686EEBC2-1](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/05265BDDZlIMG_7DF7686EEBC2-1.jpeg) + +### 代码 + +```go +func checkRecord(n int) int { + // 使用两个固定大小的数组交替使用 + before := [6]int{1, 0, 1, 0, 0, 1} + now := [6]int{} + + for i := 1; i < n; i++ { + now[0] = before[2] + now[1] = before[0] + now[2] = (before[0] + before[1] + before[2]) % 1000000007 + now[3] = before[5] + now[4] = before[3] + now[5] = (before[0] + before[1] + before[2] + before[3] + before[4] + before[5]) % 1000000007 + // 交换 before 和 now + before, now = now, before + } + + return (before[0] + before[1] + before[2] + before[3] + before[4] + before[5]) % 1000000007 +} +```