爬楼梯 Problem https://leetcode.cn/problems/climbing-stairs/description/?envType=study-plan-v2&envId=top-100-liked
Solution 爬到i阶楼梯的方法数等于i-1阶的方法数加i-2阶的方法数。第0阶和第1阶的方法数均为1。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int climbStairs (int n) { int pre0 = 1 , pre1 = 1 ; for (int i = 2 ; i <= n; i ++) { int cur = pre0 + pre1; pre0 = pre1; pre1 = cur; } return pre1; } };
杨辉三角 Problem https://leetcode.cn/problems/pascals-triangle/?envType=study-plan-v2&envId=top-100-liked
Solution 杨辉三角边缘为1,内部data[row][col] = data[row-1][col-1] + data[row-1][col]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : vector<vector<int >> generate (int numRows) { vector<vector<int >> ans (numRows); for (int row = 0 ; row < numRows; row ++) { int len = row + 1 ; ans[row].assign (len, 1 ); for (int col = 1 ; col < len-1 ; col ++) { ans[row][col] = ans[row-1 ][col-1 ] + ans[row-1 ][col]; } } return ans; } };
打家劫舍 Problem https://leetcode.cn/problems/house-robber/?envType=study-plan-v2&envId=top-100-liked
Solution 到第i间屋子时可偷的最大金额=max(到第i-1间屋子时可偷的最大金额,到第i-2间屋子时可偷的最大金额+当前屋子金额)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int rob (vector<int >& nums) { int n = nums.size (); if (n == 1 ) return nums[0 ]; int pre0 = nums[0 ], pre1 = max (nums[1 ], pre0); for (int i = 2 ; i < n; i ++) { int cur = max (pre1, pre0 + nums[i]); pre0 = pre1; pre1 = cur; } return pre1; } };
完全平方数 Problem https://leetcode.cn/problems/perfect-squares/?envType=study-plan-v2&envId=top-100-liked
Solution 当前数的最小完全平方数的数量 = min(当前数-某最小完全平方数)+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int numSquares (int n) { vector<int > count (n+1 , n) ; count[0 ] = 0 ; for (int i = 1 ; i <= n; i ++) { for (int j = 1 ; j * j <= i; j ++) { count[i] = min (count[i], count[i-j*j] + 1 ); } } return count[n]; } };
零钱兑换 Problem https://leetcode.cn/problems/coin-change/?envType=study-plan-v2&envId=top-100-liked
Solution 当前总金额最少硬币个数 = min(当前总金额-某硬币面值)+ 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int coinChange (vector<int >& coins, int amount) { int n = coins.size (); vector<int > dp (amount+1 , amount+1 ) ; dp[0 ] = 0 ; for (int i = 1 ; i <= amount; i ++) { for (int j = 0 ; j < n; j ++) { if (i - coins[j] >= 0 ) { dp[i] = min (dp[i], dp[i-coins[j]] + 1 ); } } } return dp[amount] > amount ? -1 : dp[amount]; } };
单词拆分 Problem https://leetcode.cn/problems/word-break/description/?envType=study-plan-v2&envId=top-100-liked
Solution 某字符串能拆成单词组合的条件是尾部刚好是一个wordDict里的单词,且去除这个单词后的字符串能拆成单词组合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : bool wordBreak (string s, vector<string>& wordDict) { int len = s.size (); vector<bool > dp (len+1 , false ) ; dp[0 ] = true ; for (int end = 1 ; end <= len; end ++) { for (int i = 0 ; i < wordDict.size (); i ++) { const string &word = wordDict[i]; if (word.length () <= end) { string sub_str = s.substr (end-word.length (), word.length ()); if (sub_str == word && dp[end-word.length ()]) { dp[end] = true ; } } } } return dp[len]; } };
最长递增子序列 Problem https://leetcode.cn/problems/longest-increasing-subsequence/?envType=study-plan-v2&envId=top-100-liked
Solution 整个数组中最长严格递增子序列的长度=以每个数为结尾的严格递增子序列长度中的最大值,以某个数为结尾的严格递增子序列长度=max(以前面某个小于当前数的数为结尾的长度)+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int lengthOfLIS (vector<int >& nums) { int n = nums.size (); vector<int > dp (n, 1 ) ; int ans = 0 ; for (int i = 0 ; i < n; i ++) { for (int j = 0 ; j < i; j ++) { if (nums[j] < nums[i]) { dp[i] = max (dp[i], dp[j]+1 ); } } ans = max (ans, dp[i]); } return ans; } };
乘积最大子数组 Problem https://leetcode.cn/problems/maximum-product-subarray/?envType=study-plan-v2&envId=top-100-liked
Solution 数组中乘积最大的非空连续子数组=以每个数为结尾的乘积最大非空连续子数组中的乘积最大者;考虑到负数的存在,以某个数为结尾的合法数组=max(当前数x前面的最小值,当前数x前面的最大值,当前数),由于是连续的,所以只考虑前一项即可。 多了几个奇葩样例,开long long都过不了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int maxProduct (vector<int >& nums) { int n = nums.size (); long long max_product = 1 ; long long min_product = 1 ; long long ans = nums[0 ]; for (int i = 0 ; i < n; i ++) { long long cur_max_product = max (max (nums[i] * max_product, nums[i] * min_product), (long long )nums[i]); long long cur_min_product = min (min (nums[i] * max_product, nums[i] * min_product), (long long )nums[i]); ans = max (ans, cur_max_product); max_product = cur_max_product; min_product = cur_min_product; } return ans; } };
分割等和子集 Problem https://leetcode.cn/problems/partition-equal-subset-sum/?envType=study-plan-v2&envId=top-100-liked
Solution 分割等和子集,相当于从数组中选数,使和等于总和的一半。转换为背包问题,每个数只能选一次,且总和要刚好等于目标值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution {public : bool canPartition (vector<int >& nums) { int sum = 0 ; int n = nums.size (); for (int i = 0 ; i < n; i ++) { sum += nums[i]; } if (sum & 1 ) return false ; int target_num = sum >> 1 ; vector<vector<bool >> dp (target_num+1 , vector <bool >(n, false )); for (int j = 0 ; j < n; j ++) { dp[0 ][j] = true ; } for (int i = 1 ; i <= target_num; i ++) { for (int j = 1 ; j < n; j ++) { if (i - nums[j] >= 0 ) { dp[i][j] = dp[i-nums[j]][j-1 ] | dp[i][j-1 ]; } else { dp[i][j] = dp[i][j-1 ]; } } } return dp[target_num][n-1 ]; } };
最长有效括号 Problem https://leetcode.cn/problems/longest-valid-parentheses/?envType=study-plan-v2&envId=top-100-liked
Solution 字符串中合法最长子串的长度=以每个字符为结尾的合法子串长度中的最大值。dp[i]代表以第i个字符为结尾的合法子串的长度,当前右括号和某左括号配对时,还需要把这个左括号前面的长度考虑进来,具体状态转移方程见代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : int longestValidParentheses (string s) { int n = s.size (); if (n == 0 ) return 0 ; vector<int > dp (n, 0 ) ; dp[0 ] = 0 ; int ans = 0 ; for (int i = 1 ; i < n; i ++) { if (s[i] == '(' ) { dp[i] = 0 ; } else if (s[i-1 ] == '(' ) { dp[i] = (i-2 >= 0 ? dp[i-2 ] + 2 : 2 ); } else if (s[i-1 ] == ')' && (i - dp[i-1 ] - 1 ) >= 0 && s[i - dp[i-1 ] - 1 ] == '(' ) { if (i - dp[i-1 ] - 2 >= 0 && s[i - dp[i-1 ] - 2 ] == ')' ) { dp[i] = 2 + dp[i-1 ] + dp[i - dp[i-1 ] - 2 ]; } else { dp[i] = 2 + dp[i-1 ]; } } ans = max (ans, dp[i]); } return ans; } };
不同路径 Problem https://leetcode.cn/problems/unique-paths/?envType=study-plan-v2&envId=top-100-liked
Solution dp[i][j] = dp[i-1][j] + dp[i][j-1],边界为1,可以压缩成1维
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int uniquePaths (int m, int n) { vector<int > dp (n, 1 ) ; for (int i = 1 ; i < m; i ++) { for (int j = 1 ; j < n; j ++) { dp[j] = dp[j] + dp[j-1 ]; } } return dp[n-1 ]; } };
最小路径和 Problem https://leetcode.cn/problems/minimum-path-sum/?envType=study-plan-v2&envId=top-100-liked
Solution dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : int minPathSum (vector<vector<int >>& grid) { int m = grid.size (); int n = grid[0 ].size (); vector<int > dp (n, 0 ) ; dp[0 ] = grid[0 ][0 ]; for (int j = 1 ; j < n; j ++) { dp[j] = dp[j-1 ] + grid[0 ][j]; } for (int i = 1 ; i < m; i ++) { dp[0 ] += grid[i][0 ]; for (int j = 1 ; j < n; j ++) { dp[j] = min (dp[j], dp[j-1 ]) + grid[i][j]; } } return dp[n-1 ]; } };
最长回文字串 Problem https://leetcode.cn/problems/longest-palindromic-substring/description/?envType=study-plan-v2&envId=top-100-liked
Solution 从小到大枚举字串长度,初始dp全为true,dp[i][j]代表[i, j]的子串是否为回文串,dp[i][j]=dp[i+1][j-1] && s[i] == s[j]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : string longestPalindrome (string s) { int n = s.size (); vector<vector<bool >> dp (n, vector <bool >(n, true )); int max_start = 0 ; int max_len = 1 ; for (int len = 2 ; len <= n; len ++) { for (int start = 0 ; start <= n - len; start ++) { int end = start + len - 1 ; dp[start][end] = dp[start+1 ][end-1 ] && s[start] == s[end]; if (dp[start][end]) { max_start = start; max_len = len; } } } return s.substr (max_start, max_len); } };
最长公共子序列 Problem https://leetcode.cn/problems/longest-common-subsequence/description/?envType=study-plan-v2&envId=top-100-liked
Solution 令dp[i][j]代表text1[0:i],text2[0:j]最长公共子序列长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : int longestCommonSubsequence (string text1, string text2) { int len1 = text1.length (); int len2 = text2.length (); vector<vector<int >> dp (len1+1 , vector <int >(len2+1 , 0 )); for (int i = 0 ; i < len1; i ++) { for (int j = 0 ; j < len2; j ++) { if (text1[i] == text2[j]) { dp[i+1 ][j+1 ] = max (max (dp[i][j] + 1 , dp[i+1 ][j]), dp[i][j+1 ]); } else { dp[i+1 ][j+1 ] = max (dp[i+1 ][j], dp[i][j+1 ]); } } } return dp[len1][len2]; } };
编辑距离 Problem https://leetcode.cn/problems/edit-distance/?envType=study-plan-v2&envId=top-100-liked
Solution dp[i][j]代表从word1[0:i]变换到word2[0:j]所需的最少操作数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution {public : int minDistance (string word1, string word2) { int len1 = word1.length (); int len2 = word2.length (); vector<vector<int >> dp (len1+1 , vector <int >(len2+1 , 0 )); for (int i = 0 ; i <= len1; i ++) { dp[i][0 ] = i; } for (int j = 0 ; j <= len2; j ++) { dp[0 ][j] = j; } for (int i = 1 ; i <= len1; i ++) { for (int j = 1 ; j <= len2; j ++) { int insert1 = dp[i][j-1 ] + 1 ; int insert2 = dp[i-1 ][j] + 1 ; int replace = dp[i-1 ][j-1 ] + (word1[i-1 ] == word2[j-1 ] ? 0 : 1 ); dp[i][j] = min (min (insert1, insert2), replace); } } return dp[len1][len2]; } };