70. Climbing Stairs
爬楼梯,一次可以爬一阶或两阶楼梯,爬上n阶楼梯有多少种方法?原题
斐波那契问题
1 | def fibonacci(n): |
746. Min Cost Climbing Stairs
楼梯上每层写了到达该层的卡路里,求上到顶层消耗的最小卡路里。原题
1 | Input: cost = [10, 15, 20] |
方法一:the final cost f[i]
to climb the staircase from some step i
is f[i] = cost[i] + min(f[i+1], f[i+2])
。到达一层有两种选择,一种是上一层,一种是上两层。
1 | def minCostClimbingStairs(self, cost: List[int]) -> int: |
121. Best Time to Buy and Sell Stock
买入卖出最大收益。原题
1 | Input: [7,1,5,3,6,4] |
方法一:Brute Force.其实就是求最高峰点和前面最低谷点的差。
1 | def maxProfit(self, prices: List[int]) -> int: |
1 | def maxProfit(self, prices: List[int]) -> int: |
122. Best Time to Buy and Sell Stock II
买入卖出,允许多次交易。原题
1 | Input: [7,1,5,3,6,4] |
思路:比较每两天的价格,如果是涨价了,那就把收益计算进去,否则不出手交易。
1 | def max_profit(prices): |
方法二:pairwise.
1 | def maxProfit(self, prices: List[int]) -> int: |
714. Best Time to Buy and Sell Stock with Transaction Fee
和122比较像,区别在于每次交易时有一定的手续费。
1 | Input: prices = [1, 3, 2, 8, 4, 9], fee = 2 |
方法一:dp。和抢房子有点像。
1 | def maxProfit(self, prices: List[int], fee: int) -> int: |
1 | def maxProfit(self, prices: List[int], fee: int) -> int: |
Best Time to Buy and Sell Stock III
最多允许交易两次。原题
先从左到右按照一次交易计算每天的利润。然后按照从右到左,判断如果进行第二次交易,最大的利润。
1 | def maxProfit(self, prices: List[int]) -> int: |
188. Best Time to Buy and Sell Stock IV
最多允许交易K次。
方法一:三维dp数组。
1 | def maxProfit(self, k: int, prices: List[int]) -> int: |
309. Best Time to Buy and Sell Stock with Cooldown
每次买卖股票后有一天的冷却期什么也不能干。原题
方法一:3个状态,5中状态转换。
1 | def maxProfit(self, prices: List[int]) -> int: |
LCP 19. 秋叶收藏集
杯赛里的题,说将字符串替换成r*y*r*
的形式,最少需要多少步。原题
方法一:和309非常相似,记录3种状态。
1 | def minimumOperations(self, leaves: str) -> int: |
198. House Robber
抢劫房子问题。不能连续抢劫两个挨着的房间。原题
1 | Input: [2,7,9,3,1] |
1 | f(0) = nums[0] |
方法一:递归,超时。
1 | def rob(self, nums): |
方法二:
1 | def rob(self, nums): |
213. House Robber II
与上题不同的是,所有的房子连成一个环。原题
1 | nput: [2,3,2] |
方法一:注意nums长度为1的情况。
1 | def rob(self, nums: List[int]) -> int: |
303. Range Sum Query - Immutable
给定一个数组,计算索引i, j
之间的和。原题
1 | Given nums = [-2, 0, 3, -5, 2, -1] |
思路:如果单纯采用切片计算,效率过低,题中要求sumRange调用多次。所以这里采用动态规划。
1 | class NumArray: |
91. Decode Ways
将数字翻译成字母有多少种方式。原题
1 | Input: "226" |
1 | def numDecodings(self, s: str) -> int: |
62. Unique Paths
一个矩阵中,从左上走到右下有多少种不同走法,每次只能向右或向下移动。原题
方法一:构建二维矩阵。
1 | def uniquePaths(self, m: int, n: int) -> int: |
accumulate
为此而生。
1 | [1, 1, 1, 1, 1, 1, 1] |
1 | def uniquePaths(self, m: int, n: int) -> int: |
63. Unique Paths II
和62一样,不同的是中间加了障碍1
。原题
1 | Input: |
方法一:首次AC的方法,这里采用先遍历一次记录障碍,然后初始化首行和首列,最后再求解的过程。
1 | def uniquePathsWithObstacles(self, g: List[List[int]]) -> int: |
方法二:想错了一件事情,我根本不需要去单独的设置障碍值,在遍历的时候就可以根据0来判断。
1 | def uniquePathsWithObstacles(self, g: List[List[int]]) -> int: |
120. Triangle
三角形从上到下最小路径。原题
1 | [ |
方法一:我这里使用了一个嵌套的字典保存每一行的累计最小值。
1 | def minimumTotal(self, t: List[List[int]]) -> int: |
1 | def minimumTotal(self, t: List[List[int]]) -> int: |
1 | def minimumTotal(self, t: List[List[int]]) -> int: |
方法四:我按照方法三实现了一个纯的生成器版本。iter
是防止t中只有一行的情况。这里我不确定是否将空间复杂度降低到了O(1)。
1 | def minimumTotal(self, t: List[List[int]]) -> int: |
931. Minimum Falling Path Sum
和120相似,不过形状变成了矩形。原题
1 | Input: [[1,2,3],[4,5,6],[7,8,9]] |
方法一:常规写法。
1 | def minFallingPathSum(self, A: List[List[int]]) -> int: |
方法二:错位计算的方式,这个比120三角形的要复杂一点。需要填充无穷大来使生效。
1 | def minFallingPathSum(self, A: List[List[int]]) -> int: |
1289. Minimum Falling Path Sum II
上题变形,每行找到非自己那列的元素。原题
方法一:用堆记录2个最小的值。
1 | def minFallingPathSum(self, arr: List[List[int]]) -> int: |
279. Perfect Squares
完美平方,找出n的最少的能被几个平方数相加。原题
1 | Input: n = 13 |
f(n)表示n最少的个数。f(n)=min(f(n-1²), f(n-2²)...f(0)) + 1
1 | class Solution: |
5. Longest Palindromic Substring
最长回文子字符串。原题
1 | Input: "babad" |
方法一:O(n²)的方法。非常低效的一个做法。
1 | def longestPalindrome(self, s: str) -> str: |
1 | def longestPalindrome(self, s: str) -> str: |
算法详解。这里是把一些情况做了整合。整个代码非常优雅。
1 | def longestPalindrome(self, s: str) -> str: |
1024. Video Stitching
影片剪辑,给定n组影片段,求能够拼出0~T完整影片所使用的最小段数。原题
1 | Input: clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], T = 10 |
方法一:此题竞赛时没有完成,想了字典的方法,老是纠结于怎么消除题中的[1, 5]
段,其实根本没必要,迭代的时候维护两个变量,一个是已经能组成的最大时长,另一个是当前可以延长到的最大时长。看了Lee神的答案。有个地方比较难理解,假设输入是[[0,2], [1,9],[4,6]], T=9
。按照逻辑如果全执行完,cnt应该=3。但是一旦end2
覆盖了答案就及时break掉了,所以不会再累加。如果T10,那么即使cnt为3也会返回-1。
1 | def videoStitching(self, clips: List[List[int]], T: int) -> int: |
1048. Longest String Chain
每个字符添加任意一个字符,可以组成一个字符串链。原题
1 | def longestStrChain(self, words: List[str]) -> int: |
1143. Longest Common Subsequence
最长公共子串的长度。原题
1 | Input: text1 = "abcde", text2 = "ace" |
方法一:递归
1 | import functools |
方法二:迭代。dp(i,j) means the longest common subsequence of text1[:i] and text2[:j].
1 | def longestCommonSubsequence(self, text1: str, text2: str) -> int: |
583. Delete Operation for Two Strings
最少删除多少次可以让两个字符串相等。原题
1 | Input: "sea", "eat" |
方法一:dp。这题一上来就发现和1143. Longest Common Subsequence一样。
1 | def minDistance(self, word1: str, word2: str) -> int: |
712. Minimum ASCII Delete Sum for Two Strings
删除最小的ascii码的字符,使剩下的两个字符串相等。
方法一:同583.
1 | def minimumDeleteSum(self, s1: str, s2: str) -> int: |
1312. Minimum Insertion Steps to Make a String Palindrome
将一个字符串变为回文串,最小插入字母步数。原题
1 | Input: s = "zzazz" |
方法一:和1134,Longest Common Subsequence一样,当这个字符串和他倒序的公共子串越多,需要添加的字母就越少。
1 | def minInsertions(self, s: str) -> int: |
221. Maximal Square
最大的正方形岛屿面积。原题
1 | Input: |
1 | def maximalSquare(self, g: List[List[str]]) -> int: |
1 | def maximalSquare(self, g: List[List[str]]) -> int: |
1340. Jump Game V
跳跃游戏,可以向左右d范围内矮的地方跳下。原题
1 | Input: arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2 |
1 | def maxJumps(self, arr: List[int], d: int) -> int: |
1301. Number of Paths with Max Score
左上到右下,最大值,路径中存在障碍,并且需要返回路径的个数。原题
1 | Input: board = ["E23","2X2","12S"] |
方法一:初次AC的方法。此题与剑指offer中礼物的最大值有点像,多了一个障碍,多了一种走法,多返回一个数量。这种解法对于带有障碍的问题来说 不太适合。但是速度上比方法二快了一点。
1 | def pathsWithMaxScore(self, board: List[str]) -> List[int]: |
+1
的dp。lee215的解法。3个方向的延伸放到了循环中,同时记录最大的路径个数。
1 | def pathsWithMaxScore(self, board: List[str]) -> List[int]: |
1277. Count Square Submatrices with All Ones
矩阵中最多有多少个1构成的正方形。原题
1 | Input: matrix = |
1 | def countSquares(self, mat: List[List[int]]) -> int: |
1269. Number of Ways to Stay in the Same Place After Some Steps
回到原点的走法一共有多少种,一次只能向右,向左或者停留,要求始终保持在数组范围。原题
1 | Input: steps = 3, arrLen = 2 |
方法一:找到状态转移方程,dp[p][s] = dp[p-1][s-1] + dp[p][s-1] + dp[p+1, s-1]
p代表位置,s代表步数。首部添加0方便求和。注意t+3
这个范围。
1 | def numWays(self, steps: int, n: int) -> int: |
338. Counting Bits
返回从0到num的数中,每个数二进制中含有1的个数。原题
1 | Input: 5 |
方法一:此解法用了191的暴力解法。
1 | def countBits(self, num: int) -> List[int]: |
dp[i]=dp[i//2]+i&1
1 | def countBits(self, num: int) -> List[int]: |
1262. Greatest Sum Divisible by Three
最多的元素和能被3整除。原题
1 | Input: nums = [3,6,5,1,8] |
方法一:数学方法,累加所有的数,和可能有三种情况:
- 余 0 ,刚好整除。
- 余1, 需要减去一个余1的数,或者两个余2的数。
- 余2,减去一个余2的数,或者两个余1的数。
需要注意数字是否够用。
1 | def maxSumDivThree(self, nums: List[int]) -> int: |
1 | def maxSumDivThree(self, nums: List[int]) -> int: |
72. Edit Distance
两个单词,将a变成b的最小步数,可以添加、删除,替换一个字母。原题
1 | Input: word1 = "horse", word2 = "ros" |
方法一:真是后悔没早点做这个题,这题解法下有一个方法将dp的问题从记忆化搜索的递归到实现的过程讲解的非常详细。这里从这样的一个递归开始演变。
1 | class Solution: |
到最终的形态。
1 | def minDistance(self, word1: str, word2: str) -> int: |
322. Coin Change
找零问题,给你几种面值的硬币,不限制每种硬币的个数,问组成多少钱最少需要几个硬币。原题
1 | Input: coins = [1, 2, 5], amount = 11 |
方法一:自己想的方法。
1 | def coinChange(self, coins: List[int], amount: int) -> int: |
518. Coin Change 2
找钱问题,给你几种面值的硬币,不限制每种硬币的个数,问组成多少钱有多少种方法。原题
1 | Input: amount = 5, coins = [1, 2, 5] |
方法一:背包问题,看了答案。
dp[i - 1][j]
: 完全不用当前硬币组成j有多少种组合dp[i][j - coins[i - 1]]
:使用至少一个当前硬币(与上面一条是互斥事件)组成组成j有多少组合。for循环不能写反了,比如1+5和5+1同样能组成6,被算了两次。
1 | def change(self, amount: int, coins: List[int]) -> int: |
1220. Count Vowels Permutation
元音字母的全排列,根据指定规则的,求全排列的个数。原题
1 | Input: n = 2 |
方法一:没想到居然做出来了。dp分别代表了以这些字母开头的个数。
1 | def countVowelPermutation(self, n: int) -> int: |
1 | def countVowelPermutation(self, n: int) -> int: |
368. Largest Divisible Subset
最大的整除子集。原题
1 | Input: [1,2,3] |
方法一:stefan的解法。自己没想出来。
1 | def largestDivisibleSubset(self, nums: List[int]) -> List[int]: |
5456. Kth Ancestor of a Tree Node
找出一个树节点的k个祖先。原题
1 | Input: |
方法一:这道题没有做出来,Lee的答案。用的倍增法,binary lifting.
1 | class TreeAncestor: |
1477. Find Two Non-overlapping Sub-arrays Each With Target Sum
找到数组中等于目标值的两个不重叠子数组的最小长度和。原题
1 | Input: arr = [3,2,2,4,3], target = 3 |
方法一:看了提示后使用了前后遍历法做出来的。其实有一次遍历的方式。这个方法看了挺长时间,才明白,实际上记录了一个以end为结尾的前面的所有元素最好的长度是多少。
1 | def minSumOfLengths(self, arr: List[int], target: int) -> int: |
494. Target Sum
给你一组数,用+或-连接起来最后等于target,问有多少种填法。原题
1 | Input: nums is [1, 1, 1, 1, 1], S is 3. |
方法一:记忆化搜索。
1 | def findTargetSumWays(self, nums: List[int], S: int) -> int: |
174. Dungeon Game
地牢游戏,从左上走到右下,每次只能像右或者向下,格子里会扣血和加血,问最少需要多少血,全程保持血量为1以上。原题
方法一:这道题曾经面试某公司的时候做过,只是那道题还要求返回路径,当时做的时候以为做对了。再次遇见此题时想了一晚上发现想简单了。一开始想保留两个变量,一个是最少血量,一个是累加和。然后根据最少血量判断选择走哪条路,结果一个case证明了想法是错的。还是评论区找的写法。
1 | def calculateMinimumHP(self, dungeon: List[List[int]]) -> int: |
96. Unique Binary Search Trees
不重复的二叉搜索树,1~n节点。原题
1 | Input: 3 |
方法一:这题看了答案,状态转移方程式这样的G(n)表示n个节点能组成的二叉搜索树节点个数。F(i, n)表示有n个节点时,以i为root的个数。G(n) = F(1, n) + F(2, n) + ... + F(n, n).
F(3, 7)=G(2)*G(4)
即F(i, n) = G(i-1) * G(n-i)
, 所以最后G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)
1 | def numTrees(self, n: int) -> int: |
95. Unique Binary Search Trees II
这题和96一样,要求将所有的树找出来。原题
方法一:第一次AC的方法。
1 | def generateTrees(self, n: int) -> List[TreeNode]: |
方法二:看了stefan的答案,更正一下自己的方法,lo==hi
是没必要的,之前因为先考虑这个因素,没有删掉。deepcopy可以写成别的形式,在内循环生成root节点,而不是在外层生成一个再去拷贝。记忆化搜索也是没有必要的。用时比原来快了3倍。
1 | def generateTrees(self, n: int) -> List[TreeNode]: |
337. House Robber III
抢劫房子,房子是二叉树结构,不能抢两个挨着的节点,问最多可以抢多少。原题
方法一:看了讨论区才解出来。对于一个节点来说,只有两种case:抢与不抢。将这个条件利用起来代入到递归判断中
1 | def rob(self, root: TreeNode) -> int: |
1504. Count Submatrices With All Ones
查找有多少个由1组成的子矩阵。原题
1 | Input: mat = [[1,0,1], |
方法一:竞赛时未做出来,有道类似的题是求正方形的,所以思路被限制了,找到一个状态转移方程,但是case1跑不过,未注意变量的范围,其实O(MMN)的时间复杂度也是允许的。从1D扩展的2D,然后h表示当前列k的top到down是否都是1。
1 | def numSubmat(self, mat: List[List[int]]) -> int: |
1140. Stone Game II
和1不一样,这回的规则是这样的,每次拿前m <= x <= 2m个堆,问最后Alex可以拿多少最多。原题
1 | Input: piles = [2,7,9,4,4] |
方法一:没做出来,Lee的答案看了半天,退出条件开始很难想出来。就是如果能够全拿,就退出。转移方程式这样的,A变成了一个从后累加的数组,当前的人拿i
~i+x
另外的人就是dp[i+x, max(m, x)]
1 | def stoneGameII(self, A: List[int]) -> int: |
1510. Stone Game IV
这次是有n堆石头,每次可以拿平方数堆,如果谁拿不了了,谁就输了。
1 | Input: n = 7 |
方法一:递归+记忆化,这里注意要从大到小遍历,开始写反了超时了一次。这里发现j*j
比j**2
快130ms,不明原因。240ms。
1 | def winnerSquareGame(self, n: int) -> bool: |
方法二:迭代。Lee的迭代用了1000ms,这次没我写得好。我稍微改了一下,使它降到了700ms,不过还是很慢。
1 | def winnerSquareGame(self, n: int) -> bool: |
1686. Stone Game VI
两人从任意位置拿石头,每个石头对于两个人的价值不一样。两人都按最优方式拿,最后1表示Alice赢,0表示平局。
1 | Input: aliceValues = [2,4,3], bobValues = [1,6,7] |
方法一:比赛时想出来了,里面有个贪心的思想,对于一个石头来说,每个人都优先拿对于两个人价值都高的,所以这里是以累加为条件作为排序。
1 | def stoneGameVI(self, aliceValues: List[int], bobValues: List[int]) -> int: |
1524. Number of Sub-arrays With Odd Sum
求一个数组的和为奇数的子数组的个数。原题
1 | Input: arr = [1,3,5] |
方法一:这题作为竞赛2题没做上,导致周赛排名降了不少。odd表示前i个数包含i的子数组奇数的个数。even则是偶数个数。那么当i+1为偶数时,even+1,奇数不变;当i+1为奇数时,even=odd,even=odd+1
1 | def numOfSubarrays(self, arr: List[int]) -> int: |
139. Word Break
问s是否能拆成words里的单词,单词可以重复使用。原题
1 | Input: s = "applepenapple", wordDict = ["apple", "pen"] |
方法一:还是想不到dp的解法。此题需要逆向思维。
1 | def wordBreak(self, s: str, words: List[str]) -> bool: |
方法二:递归倒是想到了。
1 | def wordBreak(self, s: str, wordDict: List[str]) -> bool: |
140. Word Break II
和上题一样,不过要求返回所有拆分的结果。原题
1 | Input: |
方法一:回溯法,这里有个case会超时,所以判断了一下s是否有单独的字符出现。这个方法不太好,LC的例子有点弱,我将用例修改为"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
超出内存限制了。好叭,方法二也没过了。
1 | def wordBreak(self, s: str, words: List[str]) -> List[str]: |
1 | def wordBreak(self, s: str, words: List[str]) -> List[str]: |
1416. Restore The Array
字符串s是有1~k个数非0开头组成的,问有多少种组成的方式。原题
1 | Input: s = "1000", k = 10000 |
方法一:首次ac的方法。第10行如果不取余,时间慢了一倍,空间多了好几倍。这个思路和Word Break有点像,dp[i]表示s[:i]总共的组成方式。
1 | def numberOfArrays(self, s: str, k: int) -> int: |
方法二:针对方法一优化,while循环看着有点乱。
1 | def numberOfArrays(self, s: str, k: int) -> int: |
1 | def numberOfArrays(self, s: str, k: int) -> int: |
1537. Get the Maximum Score
两个不重复数字的数组,从某个数组从左向右找一条路径,使值最大,如果和另一个数组有重复的数字则可以跳到另一个数组上。原题
方法一:竞赛时没有时间想,不过做起来比第3题简单。
1 | def maxSum(self, nums1: List[int], nums2: List[int]) -> int: |
1 | def maxSum(self, A: List[int], B: List[int]) -> int: |
1553. Minimum Number of Days to Eat N Oranges
每天可以吃一个橙子;或者被2整除时吃一半;或者被3整除时吃3分之2。问最少几天可以吃完原题
1 | Input: n = 10 |
方法一:比赛时未做出来,想初始化一个2*10**9的数组,结果内存溢出。而且取余也没想出来。
1 | class Solution: |
方法二:bfs,这个方法完全没有想到,就是将3种吃法放到队列中去。这种时间效率比方法一低很多。
1 | def minDays(self, n: int) -> int: |
801. Minimum Swaps To Make Sequences Increasing
最少多少次对应位置的交换可以使两个数组都严格地单调递增。原题
1 | Example: |
方法一:在提交错一次get新case后做了出来。首次AC的方法。
1 | def minSwap(self, A: List[int], B: List[int]) -> int: |
方法二:看了lee的答案,改了一下初始化的值,补负无穷是没必要的
1 | def minSwap(self, A: List[int], B: List[int]) -> int: |
837. New 21 Game
新的21点游戏,有个W面的骰子,少于K的时候要一直掷骰子,直到和超过K,问最后结果小于等于N的概率。原题
1 | Input: N = 6, K = 1, W = 10 |
1 | def new21Game(self, N: int, K: int, W: int) -> float: |
p(K) = p(K-1) / W + p(K-2) / W + p(K-3) / W + ... p(K-W) / W
,然后Wsum
其实就是p(K-1)+p(K-2)+...p(K-W)
,想象成一个W长度的滑动窗口,然后i<K
是因为如果i达到了K,就已经截止了,所以概率不再累加了。
1 | def new21Game(self, N: int, K: int, W: int) -> float: |
808. Soup Servings
有两种汤,有四种上法,如果一种汤不够,那都全都上了。问A汤先卖完的概率加上一起卖完的概率的一半,和是多少。原题
1 | Example: |
1 | def soupServings(self, N: int) -> float: |
1 | class Solution(object): |
983. Minimum Cost For Tickets
最小花费的票钱,有三种通票,分别能旅行1,7,30天,对应三种价格,要在一年中的旅行日花费最少,需要多少钱。原题
1 | Input: days = [1,4,6,7,8,20], costs = [2,7,15] |
方法一:想了一会儿就做出来了,需要注意如果不是旅行日,要等于前一天的花费。
1 | def mincostTickets(self, days: List[int], costs: List[int]) -> int: |
473. Matchsticks to Square
将长度列表的火柴棍拼成正方形。不能折断某个火柴,但可以连接。原题
1 | Input: [1,1,2,2,2] |
方法一:dfs ,效率不是很高。sort很重要,如果优先拿的不是最大的,那么将会超时。
1 | def makesquare(self, nums: List[int]) -> bool: |
A[bit]>cur
下的break,让我对输出疑惑了很久,mask是一个N长度的都是1的二进制数,每次从右到左数第[i]位设为0 表示A[i]已经使用了,cur表示当前边剩下的长度,如果为0,表示一个边已经准备好,重新再设为T。原答案用的A排序是正序,我改成了倒序,又快了一倍,但是感觉这个跟cases是密切相关的,这个方法非常快,最快仅用40ms。比方法一快得多。
1 | def makesquare(self, A: List[int]) -> bool: |
799. Champagne Tower
这题蛮有意思,说一个香槟塔,从塔尖倒酒,倒指定杯数的酒,然后问第几行第几个杯子是有多少酒。原题
方法一:想象每行前后多两个空杯,方便计算。
1 | def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: |
方法二:Lee的方法和我的差不多,但是空间上比较小。
1 | def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: |
486. Predict the Winner
两个选手每次从一堆球的两边拿,每人都是最优解,问最后A是否能赢。原题
1 | Input: [1, 5, 2] |
方法一:主要找到公式。递归。dp[i][j]
表示从i~j最大能赢多少分。
1 | def PredictTheWinner(self, nums: List[int]) -> bool: |
方法二:迭代的方法更新顺序不太好想,s表示长度。
1 | def PredictTheWinner(self, nums: List[int]) -> bool: |
576. Out of Boundary Paths
将球踢出边界的路径数量,步数在N步之内。原题
方法一:比较直观的方法。
1 | def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int: |
[3,2,3]
,第二步是[5,8,5]
,第3步是[11,12,11]
,当从2加到3步时,两边的点1~2步的结果,加了一步,可以从四周到达这个节点,两边+了一步变成2~3步的个数。然后再加上和下边界1步的个数
1 | def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int: |
688. Knight Probability in Chessboard
中国象棋的马,在棋盘上走K步,问还在棋盘上的概率。原题
方法一:和576一样。区别在于刚好K步,走法不同。
1 | def knightProbability(self, N: int, K: int, r: int, c: int) -> float: |
方法二:少了最后一步求和的过程。
1 | def knightProbability(self, N: int, K: int, r: int, c: int) -> float: |
132. Palindrome Partitioning II
最少需要多少次分割,可以将s切成的每段都是回文串。原题
1 | Input: s = "aab" |
方法一:比较暴力的方法。
1 | def minCut(self, s: str) -> int: |
1043. Partition Array for Maximum Sum
将一个数组切分, 每段可以把所有的值变成当前段最大值。每段最长不能超过k。求切分后所有数的最大的和。原题
1 | Input: arr = [1,15,7,9,2,5,10], k = 3 |
方法一:稍微想一想就想到了dp。这个AC的方法将近3s了。不过也beats了20%。索引那里需要注意是从1开始的,切片的时候得减去。
1 | def maxSumAfterPartitioning(self, A: List[int], K: int) -> int: |
1 | def maxSumAfterPartitioning(self, A: List[int], K: int) -> int: |
1 | def maxSumAfterPartitioning(self, A: List[int], K: int) -> int: |
790. Domino and Tromino Tiling
在2*N的平台上摆放两种多米诺骨牌,一共有多少种摆法。原题
1 | Example: |
方法一:这题看了答案,自己推的状态方程不太对。没考虑第2种骨牌的多种摆法。引用一张高票的手写推导图。
1 | dp[n]=dp[n-1]+dp[n-2]+ 2*(dp[n-3]+...+d[0]) |
1 | def numTilings(self, N: int) -> int: |
329. Longest Increasing Path in a Matrix
矩阵中最长的递增路径。原题
1 | Input: nums = |
方法一:回溯+记忆化搜索。这题做的时候状态不太好,导致好几次WA和超时。首先要明白几点。因为是递增,不包含重复的,所以不需要对已遍历的节点进行重复判断。对于每个点来说,它所能延长到最长的路径是一定的。所以当得到这个值之后,后续再到这个节点就无需再计算了。使用一个字典来记录它的值。
1 | def longestIncreasingPath(self, g: List[List[int]]) -> int: |
1 | def longestIncreasingPath(self, g: List[List[int]]) -> int: |
638. Shopping Offers
这题和329差不多的方法。说商店有东西打折。一些商品打包打折卖。给定每种东西的数量,问最少需要多少钱。最多只有6个商品,每样不超过6个。你不能多买,即便需要的钱更少。原题
1 | Input: [2,5], [[3,0,5],[1,2,10]], [3,2] |
方法一:思路还算清晰,但是这里因为自作聪明加了两行代码,导致最后严重超时。方法还是dfs + 记忆化。因为加了注释中的代码,这里原本想的是,索性将原价商品页当做打包卖,只不过价格不一样而已。但是这样会增加dfs中的m。最后导致了超时。
1 | def shoppingOffers(self, price: List[int], special: List[List[int]], needs: List[int]) -> int: |
1 | def shoppingOffers(self, price: List[int], special: List[List[int]], needs: List[int]) -> int: |
152. Maximum Product Subarray
最大的子数组乘积。和1567题目类似,那题是求长度,可以贪心,这题没有。原题
1 | Input: [2,3,-2,4] |
方法一:dp写法和1567dp解法相似。[-2]
的例子只需要在前面判断一下长度是否为1即可。
1 | def maxProduct(self, nums: List[int]) -> int: |
1 | def maxProduct(self, A: List[int]) -> int: |
1 | def maxProduct(self, A: List[int]) -> int: |
LCP 20. 快速公交
有这么个站点,每次从站点网下走需要inc时间,往回走需要dec时间,可以通过乘坐i公交车从移动到jum[i]*x站点,耗时为cost[i],问到达target站点最少需要多少时间。
1 | 输入:target = 612, inc = 4, dec = 5, jump = [3,6,8,11,5,10,4], cost = [4,7,6,3,7,6,4] |
方法一:记忆化搜索了。杯赛时没做出来。不好想的地方在12行,往回退的时候。
1 | def busRapidTransit(self, target: int, inc: int, dec: int, jump: List[int], cost: List[int]) -> int: |
877. Stone Game
石头游戏,一共有偶数堆,并且总数为奇数。每次拿左或者右的一堆,每次拿都是最优解,问Alex先拿能否赢。原题
1 | Input: piles = [5,3,4,5] |
方法一:还算简单的一个dp,488ms, beats40%。内存有点多120多M。思路是从两边到中间。
1 | def stoneGame(self, piles: List[int]) -> bool: |
1 | def stoneGame(self, p: List[int]) -> bool: |
1 | def stoneGame(self, p: List[int]) -> bool: |
以[5,3,4,5]
输出i, d ,dp是这样的。
1 | 0 1 [5, 3, 4, 5] 2 -2 |
方法四:对于此题来说,堆为偶数个,和为奇数。那么将其分为两组,奇数索引组合偶数索引组,假设这两个人都按照这个拿,Alex先手总能拿到多的那个。因为Alex可以保证拿到多的那组,比如[5,3,4,5]
奇数组的5+4>5+3,那么在拿走第一个5,对手只能从两个偶数组选,而无论对手选哪个偶数,Alex总有下一个奇数组的数可选。这题曾经在知乎上看到过uwi在比赛中思考了1分多钟就写出了这样的答案,不愧为常年霸榜的选手。
1 | def stoneGame(self, p: List[int]) -> bool: |
1690. Stone Game VII
此题和877题相似,区别在于拿取一个石头得分为剩余石头的总和。
1 | Input: stones = [5,3,1,4,2] |
方法一:此题竞赛没过,写了递归的超时了,迭代的状态转移方程没有找到。但是将lru_cache
最大长度改为1000
就能过,耗时6000ms。就很迷
1 | def stoneGameVII(self, stones: List[int]) -> int: |
方法二:remain
是冗余的,这个比赛时没有想好。这个方法lru_cache(None)
同样过不了。
1 | def stoneGameVII(self, stones: List[int]) -> int: |
方法三:迭代。这里想错了一件事情,dp[i][j]
表示stones[i]~stones[j]
最多的得分是多少。而不是stones[i]
开始j长度最多的得分,这里将j和d搞混了。
1 | def stoneGameVII(self, stones: List[int]) -> int: |
300. Longest Increasing Subsequence
最长的递增子序列长度。
1 | Input: [10,9,2,5,3,7,101,18] |
方法一:首次AC的方法,时间O(n^2)。
1 | def lengthOfLIS(self, nums: List[int]) -> int: |
1 | def lengthOfLIS(self, nums: List[int]) -> int: |
假设输入是[10,9,2,5,3,7,101,18,-1,0,1]
,tails这样变化,当出现一个较小的数,找到位置并替换,这样tails有效的部分还是有序的。
1 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
1 | def lengthOfLIS(self, nums: List[int]) -> int: |
673. Number of Longest Increasing Subsequence
求最长递增子序列的个数。和300很像,基于300上进行修改。
方法一:基于300方法一进行修改,dp元素保存两个信息,一个是最大长度,一个是数量。思考的时候要想如果dp[-1][-1]
不能直接求得值的话,要考虑重新遍历一遍。
1 | def findNumberOfLIS(self, nums: List[int]) -> int: |
方法二:进行了一些优化。比方法一快了300ms,写完之后和评论区方法比较,明明是一样的,不知道为啥慢了100ms,后来一行一行代码进行替换,调了半天,发现注释部分的代码要慢100ms,刷新了我的认知,为什么数组下标取值会慢这么多?这也太玄学了。
1 | def findNumberOfLIS(self, nums: List[int]) -> int: |
dp[i][x]
表示长度为i的子序列以x结尾的有多少个。
1 | def findNumberOfLIS(self, nums: List[int]) -> int: |
面试题 17.08. 马戏团人塔
300的变种,比较两个维度。说人可以叠罗汉,但是只能将高度和体重都大的人放在下面。
方法一:这题测试用例n^2的写法过不了。这题比堆箱子简单一点。将高度升序排列,体重降序排列。就可以按照体重来求最长上升子序列了。体重降序可以避免了高度相同的人导致结果错误。评论区学了一个方法。
1 | 输入样例:[[4,5],[4,6],[6,7],[2,3],[1,1]] |
1 | def bestSeqAtIndex(self, height: List[int], weight: List[int]) -> int: |
面试题 08.13. 堆箱子
这题和300题很像,是一个变种题。要把长宽高都大的放在底层,问最多可以叠多高。
方法一:和300一样,但是要排序,因为箱子可以随便选,这里有点迷,我一直没有想明白为什么zip会超时,而直接数组比较就不会超时。我想了想300题的NlogN的解法,但在此题上并不适用。
1 | def pileBox(self, box: List[List[int]]) -> int: |
方法二:sort时以长度升序,宽度降序。这样比较的时候可以不用比较长度了,降序是为了适应长度相等的情况。
1 | class Solution: |
1594. Maximum Non Negative Product in a Matrix
最大的从左上到右下的乘积。
方法一:这题想着迭代方法,竞赛的时候差了一行代码,时间就到了。递归的思路好想。
1 | def maxProductPath(self, g: List[List[int]]) -> int: |
方法二:迭代的初识值没想好,想着赋值成(g[0][0], float('inf'))
了,实际上(g[0][0], g[0][0])
就对了。
1 | def maxProductPath(self, g: List[List[int]]) -> int: |
面试题 08.14. 布尔运算
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
1 | 输入: s = "1^0|0|1", result = 0 |
方法一:我开始想的是将0,1的个数都分别计算。但是效率却不高。时间只beats5%。
1 | def countEval(self, s: str, result: int) -> int: |
方法二:根据评论区的方法,找到自己方法的性能瓶颈。在于大量的创建字符串执行eval
,将这里改为字典映射后,从500ms减少为90ms。
1 | def countEval(self, s: str, result: int) -> int: |
面试题 17.13. 恢复空格
你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子”I reset the computer. It still didn’t boot!”已经变成了”iresetthecomputeritstilldidntboot”。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
方法一:特别慢,虽然ac了。
1 | class Solution: |
1 | def respace(self, dictionary: List[str], sentence: str) -> int: |
1 | def respace(self, dictionary: List[str], sentence: str) -> int: |
1611. Minimum One Bit Operations to Make Integers Zero
最少需要多少步能将数变成0。两种操作方式,一种翻转最低位的数字。一种是找到x100..00(n个0,n可以=0)
的x位翻转。
方法一:这题竞赛时想了个七七八八,还是没写出来。总体来说此题不难,比第3题简单。
分为这样的步骤。1XXXXXXX -> 11000000 -> 10000000 -> 0
1XXXXXX -> 1100000 needs minimumOneBitOperations(1XXXXXX ^ 1100000), 这步没有想明白,可以递归,因为想让1XXXXXX ^ 1100000 == 0,就只能让 1XXXXXX变为1100000 。所以这里是等价的。
1100000 -> 100000 needs 1 operation.
100000 -> 0, where 100000 is 2^k, needs 2^(k-1) - 1 operations.
1 | class Solution: |
1027. Longest Arithmetic Subsequence
最长的等差子序列长度。
1 | Input: A = [9,4,7,2,10] |
方法一:O(N^2)的方法。时间beats80%, 2.6s。这里试了一下ans = max([ans] + list(dp[i].values()))
居然要4s多。Lee这里使用了一个tuple作为key,但是要慢一点,3s。
1 | def longestArithSeqLength(self, A: List[int]) -> int: |
416. 分割等和子集
能否将数组分为两个和相等的子数组。经典的0-1背包问题。
方法一:看了题解做上的,主要没想到怎么将它转化为背包问题的。它其实等价于从数组中选出一些数字,使得这些数的和为数组和的一半。
1 | def canPartition(self, nums: List[int]) -> bool: |
方法二:方法一空间优化。正序会对结果造成影响,导致计算错误。
1 | def canPartition(self, nums: List[int]) -> bool: |
bool(mask>>k & 1)
表示是否可以组成和为k。python得益于int没有长度限制,所以不用转成数组的形式。
1 | def canPartition(self, nums: List[int]) -> bool: |
方法四:可以写成一行,评论中大神的写法和我的写法。
1 | def canPartition(self, nums: List[int]) -> bool: |
128. Longest Consecutive Sequence
求数组中最长的连续序列,可以打乱顺序。要求时间复杂度O(n)。
1 | Input: [100, 4, 200, 1, 3, 2] |
方法一:Union-Find。没想到能用并查集来求,看了标签提示才恍然大悟。一开始写错了,把num和num-1也相连了,这样1和3就被连到了一起,注意一下空输入。
1 | def longestConsecutive(self, nums: List[int]) -> int: |
1 | def longestConsecutive(self, nums: List[int]) -> int: |
1 | def longestConsecutive(self, nums: List[int]) -> int: |
1314. Matrix Block Sum
矩阵块的求和。原题
1 | Input: mat = [[1,2,3],[4,5,6],[7,8,9]], K = 1 |
方法一:竞赛时暴力法过的。这里实际上求和时可以利用之前的和。
1 | def matrixBlockSum(self, mat: List[List[int]], k: int) -> List[List[int]]: |
1738. Find Kth Largest XOR Coordinate Value
找到矩阵中第K大的值,值是指坐标(a,b)所有妈祖0<=i<=a<m,0<=j<=b<n条件的异或和。
1 | Input: matrix = [[5,2],[1,6]], k = 1 |
方法一:dp,和1314很像。比赛时没有时间做。
1 | def kthLargestValue(self, matrix: List[List[int]], k: int) -> int: |
1626. Best Team With No Conflicts
有这样一个球队,每个人有一个自己的得分。当一个团队中有一个年轻的球员是比自己年长球员分多时,这两个球员就会打架。所以为了避免这样的情况,选出一个最大得分的团队,问得分是多少。
1 | Input: scores = [1,2,3,5], ages = [8,9,10,1] |
方法一:这题因为竞赛时卡在第二题上,所以没有时间来做了。和俄罗斯套娃问题很像。当球员按照年龄,得分排好序后。问题转化为了一个最长上升子序列和的问题。时间复杂度是O(n^2)。
1 | def bestTeamScore(self, scores: List[int], ages: List[int]) -> int: |
823. Binary Trees With Factors
给你一些大于1的数,要求用这些数组成一个树,树的父节点为两个子节点的乘积。问有多少种组成方式。
1 | Input: A = [2, 4, 5, 10] |
方法一:很容易想到dp,以每个点为父节点,看组成子节点的组合有多少种,然后相乘。400ms
1 | def numFactoredBinaryTrees(self, A: List[int]) -> int: |
1 | def numFactoredBinaryTrees(self, A: List[int]) -> int: |
方法三:Lee的写法。但是运行时间比方法一略优,时间相当于方法二的2倍。336ms
1 | def numFactoredBinaryTrees(self, A: List[int]) -> int: |
方法四:再尽量简洁的情况下,保证效率,使用OrderedDict保证key的顺序。192ms。
1 | def numFactoredBinaryTrees(self, A: List[int]) -> int: |
### 740. Delete and Earn
#### 给定一个整数数组 nums ,你可以对它进行一些操作。每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除每个等于 nums[i] - 1 或 nums[i] + 1 的元素。返回你能通过这些操作获得的最大点数。
1 | Input: nums = [2, 2, 3, 3, 3, 4] |
方法一:这题二十分钟AC的。首先将数组分成几个连续的段,再求每段的最大值,和抢房子问题一样。
1 | def deleteAndEarn(self, nums: List[int]) -> int: |
方法二:看到有大神能用四行写出来,于是我不服。
1 | def deleteAndEarn(self, nums: List[int]) -> int: |
### 413. Arithmetic Slices
#### 一个等差数列,至少需要三个数字,每两个挨着的数字差一样。求有多少个连续的等差数列。
1 | A = [1, 2, 3, 4] |
方法一:dp。模拟时间内没做出来是因为忽略了连续。仔细审题后10分钟AC。
1 | def numberOfArithmeticSlices(self, A: List[int]) -> int: |
方法二:双指针。
1 | def numberOfArithmeticSlices(self, A: List[int]) -> int: |
### 1671. Minimum Number of Removals to Make Mountain Array
#### 最少的删除步骤使得变成山峰数组。
1 | Input: nums = [2,1,1,5,6,2,3,1] |
方法一:竞赛时没做出来,想成和941,845题中的思路了。其实是LIS问题,最长上升子序列。这里需要注意一点就是要以当前节点为结尾的最长上升子序列,而不是
len(ans)
。时间复杂度O(n^2logn)。1 | def minimumMountainRemovals(self, nums: List[int]) -> int: |
方法二:dp。
### 376. Wiggle Subsequence
#### 最长的摆动序列长度,和LIS最长上升子序列问题一样。
1 | Input: [1,17,5,10,13,15,10,5,16,8] |
方法一:dp。一开始用了n^2的方法,其实没有必要。
1 | def wiggleMaxLength(self, nums: List[int]) -> int: |
方法二:重复的数字是不需要考虑的。这里做了一次错位。为什么最后一行是
or
,因为这里对于这个调教a<b>c
和a>b<c
来说,有一部分重复的比较在里面。1 | def wiggleMaxLength(self, nums: List[int]) -> int: |
### 1706. Where Will the Ball Fall
#### 每列的球能落到最下面哪一列,到不了的话返回-1
1 | Input: grid = [[1,1,1,-1,-1],[1,1,1,-1,-1],[-1,-1,-1,1,1],[1,1,1,1,-1],[-1,-1,-1,-1,-1]] |
方法一:比赛时没做出来,觉得和959很像,所以用并查集去想,结果是不对的。因为并查集可以向上走,而小球只能向下落。
这题应该是dfs或者Dp。
1 | def findBall(self, grid: List[List[int]]) -> List[int]: |
方法二:Lee的方法,如果小球往左或右移动,那么隔板必须是相同的。
1 | def findBall(self, grid: List[List[int]]) -> List[int]: |
### 1463. Cherry Pickup II
#### 收集樱桃,每个机器人只能向左下,下,右下移动收集。两个机器人走到同一位置,只能收集一次。
1 | Input: grid = [[3,1,1],[2,5,1],[1,5,5],[2,1,1]] |
方法一:dp。
dp[i][j1][j2]
表示i行,robot1在j1,robot2在j2时的结果。2600ms。1 | def cherryPickup(self, grid: List[List[int]]) -> int: |
方法二:记忆化搜索。1000ms。
1 | def cherryPickup(self, grid: List[List[int]]) -> int: |
### 1745. Palindrome Partitioning IV
#### 能否将字符串分为三个回文串。
1 | Input: s = "abcbdd" |
方法一:竞赛没时间做,其实比3题简单,亏了。N2时间复杂度可过。只要可以从O(1)时间得到s[i:j]是否是回文串就可以了。
而这个可以通过N2的动态规划来实现。
1 | def checkPartitioning(self, s: str) -> bool: |
### 1671. Minimum Number of Removals to Make Mountain Array
#### 最少的删除使数组变为山峰数组。
1 | Input: nums = [2,1,1,5,6,2,3,1] |
方法一:和LIS问题一样,找到2个方向的最长上升子序列,然后求和用总数相减,
>=2
表示既要有上升又要有下降。1 | def minimumMountainRemovals(self, nums: List[int]) -> int: |
方法二:nlogn的方法。使用二分法。
1 | def minimumMountainRemovals(self, nums: List[int]) -> int: |
### 1770. Maximum Score from Performing Multiplication Operations
#### 每次从数组前后拿出一个数和multiplires按序相乘,求累加和最大为多少。
1 | Input: nums = [1,2,3], multipliers = [3,2,1] |
方法一:比赛时没有AC,超时了,如果将
lru_cache(None)
改为lru_cache(M)
可以减少耗时。这里LC各个testcase之间是不会清掉cache的,所以有时cache长度过长会导致超时。1 | def maximumScore(self, nums: List[int], mult: List[int]) -> int: |
方法二:自底向上的方法很难想。
1 | def maximumScore(self, nums: List[int], multipliers: List[int]) -> int: |
### 1799. Maximize Score After N Operations
#### N步操作的最大分。每次能从数组中选出一对数,次数这两个数的最大公约数累加。
1 | Input: nums = [1,2,3,4,5,6] |
方法一:竞赛没有时间做。记忆化搜索就能过。
1 | def maxScore(self, nums: List[int]) -> int: |
### 1824. Minimum Sideway Jumps
#### 青蛙🐸最小横跳几次能到达终点。
1 | Input: obstacles = [0,1,2,3,0] |
方法一:比赛时错了一次。
1 | def minSideJumps(self, obstacles: List[int]) -> int: |
方法二:当前不是障碍时,才能计算min。这里使用取余来简化代码
1 | def minSideJumps(self, A: List[int]) -> int: |
### 1815. Maximum Number of Groups Getting Fresh Donuts
#### 有一个甜甜圈商店,每批次都烤 batchSize 个甜甜圈。这个店铺有个规则,就是在烤一批新的甜甜圈时,之前 所有 甜甜圈都必须已经全部销售完毕。给你一个整数 batchSize 和一个整数数组 groups ,数组中的每个整数都代表一批前来购买甜甜圈的顾客,其中 groups[i] 表示这一批顾客的人数。每一位顾客都恰好只要一个甜甜圈。当有一批顾客来到商店时,他们所有人都必须在下一批顾客来之前购买完甜甜圈。如果一批顾客中第一位顾客得到的甜甜圈不是上一组剩下的,那么这一组人都会很开心。你可以随意安排每批顾客到来的顺序。请你返回在此前提下,最多 有多少组人会感到开心。
1 | Input: batchSize = 3, groups = [1,2,3,4,5,6] |
方法一:此题的动态规划有点难想。这里学到一个新的方法:模拟退火算法,是一种随机算法。因为最后的结果是有顺序的,通过随机交换两个元素,比较结果;如果这个结果是比较优的,那么保留操作。如果较差,则以一定概率保留操作,以跳出局部最优解,从而得到全局最优解。
1 | def maxHappyGroups(self, batchSize: int, groups: List[int]) -> int: |
### 474. Ones and Zeroes
#### 最多包含m个0,n个1的最多的子集个数有多少。
1 | Input: strs = ["10","0001","111001","1","0"], m = 5, n = 3 |
方法一:可以dfs+记忆化。
1 | def findMaxForm(self, strs: List[str], m: int, n: int) -> int: |
方法二: 这是一个背包问题。要将原问题转换为「背包问题」,往往需要从题目中抽象出「价值」与「成本」的概念。
这道题如果抽象成「背包问题」的话,应该是:
每个字符串的价值都是 1(对答案的贡献都是 1),选择的成本是该字符串中 1 的数量和 0 的数量。
问我们在 1 的数量不超过 m,0 的数量不超过 n 的条件下,最大价值是多少。
1 | def findMaxForm(self, strs: List[str], m: int, n: int) -> int: |
方法三:可以倒序,节省一层空间。
1 | def findMaxForm(self, strs: List[str], m: int, n: int) -> int: |
### 2369. Check if There is a Valid Partition For The Array
#### 判断数组是否符合要求。
1 | Input: nums = [4,4,4,5,6] |
方法一: dp,比赛时过了,不过空间需要优化。
1 | def validPartition(self, nums: List[int]) -> bool: |
方法二:lee215的写法。因为条件最多用到3个连续元素,所以长度为4的数组就够用了。
1 | def validPartition(self, A: List[int]) -> bool: |
### 2370. Longest Ideal Subsequence
#### 最长的理想子序列的长度。子序列中小写字母的ascii码小于等于k。
1 | Input: s = "acfgbd", k = 2 |
方法一:竞赛时的方法。空间有很大优化空间。时间4000ms
1 | def longestIdealString(self, s: str, k: int) -> int: |
方法二:写法优化,时间600ms。
1 | def longestIdealString(self, s: str, k: int) -> int: |
940. Distinct Subsequences II
一个字符串不同的子序列有多少种。
1 | Input: s = "aba" |
方法一:关键点在于,每增加一个字符都可以选择增加到原有的字符上,如果不考虑重复的话,总数为2*n+1,重复的字符串,第二次出现的时候,恰好上该字符上次出现时增加的次数,记作repeat_cnt。
1 | def distinctSubseqII(self, s: str) -> int: |
1012. 至少有 1 位重复的数字
给定正整数 n
,返回在 [1, n]
范围内具有 至少 1 位 重复数字的正整数的个数。
方法一:数位DP,模板。
1 | def numDupDigitsAtMostN(self, n: int) -> int: |