【600】 Non-negative Integers without Consecutive Ones算法
【629】 K Inverse Pairs Array數組
【638】 Shopping Offersapp
【639】 Decode Ways IIide
【646】 Maximum Length of Pair Chainoop
【647】 Palindromic Substrings優化
給定一個字符串,判斷有多少子串是 palindromic 的,子串就算全部字母都相同,可是開始和結束位置的下標不一樣,也算不一樣的子串。ui
題解:dp, 時間複雜度是 O(N^2),dp[i][j] 表明 s[i..j] 是否是 迴文串。google
1 class Solution { 2 public: 3 int countSubstrings(string s) { 4 const int n = s.size(); 5 vector<vector<int>> dp(n, vector<int>(n, 0)); 6 int res = 0; 7 for (int i = 0; i < n; ++i) { 8 dp[i][i] = 1; 9 res++; 10 if (i + 1 < n && s[i] == s[i+1]) { 11 dp[i][i+1] = 1; 12 res++; 13 } 14 } 15 for (int i = n - 1; i >=0 ; --i) { 16 for (int j = i + 1; j < n; ++j) { 17 if (dp[i+1][j-1] == 1 && s[i] == s[j]) { 18 dp[i][j] = 1; 19 res++; 20 } 21 } 22 } 23 return res; 24 } 25 };
【650】 2 Keys Keyboard (2019年2月20日,谷歌tag,M)spa
假設你有個鍵盤,上面只有兩個操做:code
(1) copy all: You can copy all the characters present on the notepad (partial copy is not allowed).
(2) paste: You can paste the characters which are copied last time.
問一開始給你一個 A, 問複製到 n 個A至少須要幾步?
題解:dp[i] 表示複製到 i 個A 至少須要幾步。 dp[i] = max(dp[k] + 1 + (i / k - 1)) 其中 i 可以整除 k。
1 class Solution { 2 public: 3 int minSteps(int n) { 4 //dp[i] 表明生成 i 個A,須要的最少步數。 5 vector<int> dp(n+1, INT_MAX); 6 dp[0] = 0, dp[1] = 0; 7 for (int i = 2; i <= n; ++i) { 8 for (int k = 1; k < i; ++k) { 9 if (i % k == 0) { 10 int times = (i - k) / k; 11 dp[i] = min(dp[i], dp[k] + 1 + times); 12 } 13 } 14 } 15 return dp[n]; 16 } 17 };
【651】 4 Keys Keyboard (2019年2月20日,谷歌tag,M)
假設你有一個特殊鍵盤,只有四個鍵。
Key 1: (A): Print one 'A' on screen.
Key 2: (Ctrl-A): Select the whole screen.
Key 3: (Ctrl-C): Copy selection to buffer.
Key 4: (Ctrl-V): Print buffer on screen appending it after what has already been printed.
你只能按下 N 次鍵盤,問你能得到最長的 A 是多長。
Input: N = 7
Output: 9
Explanation:
We can at most get 9 A's on screen by pressing following key sequence:
A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V
題解:經過觀察例子咱們能夠發現,咱們令 dp[i] 表明按下 i 個鍵以後的最長的字符串的長度,轉移方程: 若是第 i 個鍵按下的是key1的話,dp[i] = dp[i-1] + 1,
若是第 i 個鍵按下的是 key4 的話,咱們須要從前面找到一個鍵(假設是第 k 個鍵),而後從這個鍵開始,重複key2, key3, key4,key4,key4...... 因此咱們這個時候,dp[i] = max(dp[k] * (x-k-1)) (1 <= k < x)
因此經過這 2-for loop 就能夠解決這個問題。時間複雜度是O(N^2)
1 class Solution { 2 public: 3 /* 4 dp[0] = 0; 5 dp[1] = 1; 6 dp[2] = 2; 7 dp[3] = 3; 8 dp[4] = 4;(2) 9 dp[5] = 5;(4,3) 10 dp[6] = 6;(6,4 11 dp[7] = 5, 8, 9 12 */ 13 int maxA(int N) { 14 vector<int> dp(N+1, 0); 15 for (int i = 1; i <= N; ++i) { 16 dp[i] = i; 17 for (int j = 1; j < i; ++j) { 18 dp[i] = max(dp[j] * (i - j - 1), dp[i]); 19 } 20 } 21 return dp[N]; 22 } 23 };
【656】 Coin Path
【664】 Strange Printer (2019年4月9日)
There is a strange printer with the following two special requirements:
Given a string consists of lower English letters only, your job is to count the minimum number of turns the printer needed in order to print it.
Example 1: Input: "aaabbb" Output: 2 Explanation: Print "aaa" first and then print "bbb". Example 2: Input: "aba" Output: 2 Explanation: Print "aaa" first and then print "b" from the second place of the string, which will cover the existing character 'a'.
題解:本題用dp作。dp[i][j] 表示打印 s[i] 到 s[j] 的最短打印的次數。
轉移方程爲: dp[i][j] = min(dp[i][j], temp), 其中,if (s[k] == s[j]) { temp = dp[i][k] + dp[k+1][j] - 1 } else {temp = dp[i][k] + dp[k+1][j]}
1 class Solution { 2 public: 3 int strangePrinter(string s) { 4 const int n = s.size(); 5 if (n == 0) {return 0;} 6 vector<vector<int>> dp(n, vector<int>(n, INT_MAX)); 7 for (int i = 0; i < n; ++i) { 8 dp[i][i] = 1; 9 if (i+1 < n) { 10 dp[i][i+1] = (s[i] == s[i + 1]) ? 1 : 2; 11 } 12 } 13 for (int len = 1; len <= n; ++len) { 14 for (int i = 0; i + len <= n; ++i) { 15 int j = i + len - 1; 16 dp[i][j] = len; 17 for (int k = i; k < j; ++k) { 18 int temp = dp[i][k] + dp[k+1][j]; 19 if (s[k] == s[j]) {temp--;} 20 dp[i][j] = min(dp[i][j], temp); 21 } 22 } 23 } 24 return dp[0][n-1]; 25 } 26 };
【673】 Number of Longest Increasing Subsequence
【688】 Knight Probability in Chessboard
【689】 Maximum Sum of 3 Non-Overlapping Subarrays
【691】 Stickers to Spell Word
【698】 Partition to K Equal Sum Subsets
【712】 Minimum ASCII Delete Sum for Two Strings (2018年12月21日,算法羣)
給了兩個字符串 s1 和 s2,要從 s1 和 s2 中刪除一些字符使得這兩個字符串相等。求最小的刪除字符的 ACSII 的和。
題解:字符串的編輯距離的變種。dp[i][j] 表示從 s1[0..i-1] 到 s2[0..j-1] 這兩個字符串相等所求的最小和。轉移方程爲: if (s1[i-1] == s2[j-1]) {dp[i][j] = dp[i-1][j-1]}, else {dp[i][j] = min(dp[i-1][j] + (int)s1[i-1], dp[i][j-1] + (int)s2[j-1])}
1 class Solution { 2 public: 3 int minimumDeleteSum(string s1, string s2) { 4 const int n1 = s1.size(), n2 = s2.size(); 5 vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, INT_MAX)); 6 dp[0][0] = 0; 7 for (int i = 1; i <= n1; ++i) { 8 dp[i][0] = dp[i-1][0] + (int)s1[i-1]; 9 } 10 for (int j = 1; j <= n2; ++j) { 11 dp[0][j] = dp[0][j-1] + (int)s2[j-1]; 12 } 13 for (int i = 1; i <= n1; ++i) { 14 for (int j = 1; j <= n2; ++j) { 15 if (s1[i-1] == s2[j-1]) { 16 //dp[i][j] = min(dp[i][j], min(dp[i-1][j] + (int)s1[i-1], dp[i][j-1] + (int)s2[j-1])); 17 dp[i][j] = min(dp[i][j], dp[i-1][j-1]); 18 } else { 19 dp[i][j] = min(dp[i][j], min(dp[i-1][j] + (int)s1[i-1], dp[i][j-1] + (int)s2[j-1])); 20 } 21 } 22 } 23 return dp[n1][n2]; 24 } 25 };
【714】 Best Time to Buy and Sell Stock with Transaction Fee (算法羣 2018年10月22日題目)
仍是股票買賣的相關問題,給了一個股票價格的數組,prices[i] 表明第 i 天的股票價格,每次賣出要交手續費,問最後的max profit 是多少。
題解:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108892/Java-DP-solution-O(n)-to-O(1)-space
這題能夠用滾動數組(兩個變量優化成O(1)的空間複雜度),可是優化後的很差理解,並且這種優化也不算什麼高端技巧。因此咱們這裏先討論下這題 dp 的本質。
咱們第 i 天要麼持有股票,要麼不持有股票。 咱們用 hold[i] 表示第 i 天持有股票的 max profit, unhold[i] 表示第 i 天不持有股票的 max profit。
那麼轉移方程能夠這麼理解: 咱們在第 i 天有兩種狀況,要麼咱們持有股票,要麼咱們不持有股票。
1. 第 i 天持有股票的狀況下, hold[i] = max(hold[i-1], unhold[i-1] - prices[i]) //意思是說咱們要麼第 i-1 天就持有股票, 第 i 天啥也不幹; 要麼咱們在第 i 天買了股票
2.第 i 天不持有股票的狀況下, unhold[i] = max(unhold[i-1], hold[i-1] + prices[i] - fee) //意思是說咱們 第 i-1 天就不持有股票了,第 i 天啥也不幹; 要們咱們在第 i 天賣了股票
如今的時間複雜度是O(N), 空間複雜度是O(1)。能夠用兩個變量優化兩個數組。
1 class Solution { 2 public: 3 //hold[i] represents max profit of holding stock until day i 4 //nothold[i] represents max profit of not holding stock until day i 5 //transaction function: for each day i, we have 2 situations. 6 //1. hold stock in day i (you can either do nothing in day i or buy stock int day i) 7 // hold[i] = max(hold[i-1], nothold[i-1]-price[i]) 8 //2. not hold stock in day i (you can either do nothing in day i or sell stock in day i) 9 // nothold[i] = max(nothold[i-1], hold[i-1] + price[i] - fee)
10
11 int maxProfit(vector<int>& prices, int fee) { 12 const int n = prices.size(); 13 if (n <= 1) { return 0; } 14 vector<int> hold(n, 0), unhold(n, 0); 15 hold[0] = -prices[0], unhold[0] = 0; 16 for (int i = 1; i < n; ++i) { 17 hold[i] = max(hold[i-1], unhold[i-1] - prices[i]); 18 unhold[i] = max(unhold[i-1], hold[i-1] + prices[i] - fee); 19 } 20 return unhold[n-1]; 21 } 22 };
【718】 Maximum Length of Repeated Subarray (2019年3月29日)
給定兩個數組,返回他們最長的相同子數組的長度。
Example 1: Input: A: [1,2,3,2,1] B: [3,2,1,4,7] Output: 3 Explanation: The repeated subarray with maximum length is [3, 2, 1].
題解:我一開始對example的例子理解有問題,我一直覺得是A的前三個元素和B的前三個元素能造成 repeated subarray。可是其實不是這樣的,實際上是 A 的後三個元素的[3,2,1] 和 B 的前三個元素的[3,2,1] 造成了一個 repeated subarray。
因此dp的定義和轉移方程這樣看也就比較明確了,dp[i][j] 表明 包含 A[i-1] 和 B[j-1] 的最長的 repeated subarray 的長度。
因此轉移方程是: if (A[i-1] == B[j-1]) { dp[i][j] = dp[i-1][j-1] + 1; }
1 class Solution { 2 public: 3 int findLength(vector<int>& A, vector<int>& B) { 4 int lenA = A.size(), lenB = B.size(); 5 vector<vector<int>> dp(lenA+1, vector<int>(lenB+1, 0)); 6 int res(0); 7 for (int i = 1; i <= lenA; ++i) { 8 for (int j = 1; j <= lenB; ++j) { 9 if (A[i-1] == B[j-1]) { 10 dp[i][j] = 1 + dp[i-1][j-1]; 11 res = max(res, dp[i][j]); 12 } 13 } 14 } 15 return res; 16 } 17 };
【727】 Minimum Window Subsequence
【730】 Count Different Palindromic Subsequences
【740】 Delete and Earn (2019年5月17日,算法羣)
給了一個整數數組 nums,操做規則以下:
In each operation, you pick any nums[i]
and delete it to earn nums[i]
points. After, you must delete every element equal to nums[i] - 1
or nums[i] + 1
.
You start with 0 points. Return the maximum number of points you can earn by applying such operations.
Example 1: Input: nums = [3, 4, 2] Output: 6 Explanation: Delete 4 to earn 4 points, consequently 3 is also deleted. Then, delete 2 to earn 2 points. 6 total points are earned. Example 2: Input: nums = [2, 2, 3, 3, 3, 4] Output: 9 Explanation: Delete 3 to earn 3 points, deleting both 2's and the 4. Then, delete 3 again to earn 3 points, and 3 again to earn 3 points. 9 total points are earned.
題解:咱們能夠雙狀態表示這個題,先作個頻率統計,再按照 key 從小到大排序,而後 p 表明算上當前 key 能取得的最大值,q 表明不計算當前的 key 取得的最大值。
狀態轉移方程如代碼所示:
1 //time complexity: O(N) 2 class Solution { 3 public: 4 int deleteAndEarn(vector<int>& nums) { 5 const int n = nums.size(); 6 map<int, int> freq; 7 for (auto& num : nums) { freq[num]++; } 8 vector<pair<int, int>> array; 9 for (auto p : freq) { 10 array.push_back(p); 11 } 12 if (array.empty()) {return 0;} 13 int p = array[0].first * array[0].second, q = 0; //p 表明選了當前這個值, q 表明不選當前這個值 14 // cout << "p = " << p << " " << "q = " << q << endl; 15 for (int i = 1; i < array.size(); ++i) { 16 int temp_p = p, temp_q = q; 17 if (array[i].first != array[i-1].first + 1) { //can take array[i] 18 p = max(temp_p, temp_q) + array[i].first * array[i].second; 19 q = max(temp_p, temp_q); 20 } else { 21 p = temp_q + array[i].first * array[i].second; 22 q = max(temp_p, temp_q); 23 } 24 } 25 return max(p, q); 26 } 27 };
【741】 Cherry Pickup (2019年1月12日,算法羣)
今天心情不好,抄的答案==,有點upset
【746】 Min Cost Climbing Stairs
【750】 Number Of Corner Rectangles
【764】 Largest Plus Sign
【787】 Cheapest Flights Within K Stops(2019年2月9日,谷歌tag)
給了一個有向圖,邊上有權重,問從 src 到 dst 最多通過 K 個站的最少花費。
題解:dp[k][v] 表示從 src 開始到 v 通過 k 個站的最小花費。dp[k][v] = min(dp[k][v], dp[k-1][u] + cost[u][v])。時間複雜度是 O(K*n*n)
初始化 cost[u][v] 表示從 u 到 v 的花費。(邊上的權重)dp[0][src] = 0。dp[0][v] = cost[u][v] (u = src)
1 //time complexity: O(k*n*n) 2 class Solution { 3 public: 4 int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) { 5 vector<vector<int>> dp(K + 1, vector<int>(n, INT_MAX)); 6 vector<vector<int>> cost(n, vector<int>(n, INT_MAX)); 7 //init 8 int ret = INT_MAX; 9 for (auto& f : flights) { 10 int u = f[0], v = f[1], w = f[2]; 11 if (u == src) { 12 dp[0][v] = w; 13 if (v == dst) {ret = w;} 14 } 15 cost[u][v] = w; 16 } 17 for (int k = 1; k <= K; ++k) { 18 for (int d = 0; d < n; ++d) { 19 for (int u = 0; u < n; ++u) { 20 if (cost[u][d] == INT_MAX || dp[k-1][u] == INT_MAX) {continue;} 21 dp[k][d] = min(dp[k][d], dp[k-1][u] + cost[u][d]); 22 } 23 if (d == dst) { ret = min(ret, dp[k][d]); } 24 } 25 } 26 if (ret == INT_MAX) { ret = -1; } 27 return ret; 28 } 29 };
【790】 Domino and Tromino Tiling
【801】 Minimum Swaps To Make Sequences Increasing
【808】 Soup Servings (2019年1月4日,算法羣,第一次作,須要複習)
給了 A,B 兩種 soup,一開始都有 N ml。有以下四種選擇,每種選擇的機率都是 25%。
若是在配比中有一種soup的劑量不夠調一份的,就用剩下全部的調一份。返回 soup A 先空的機率 加上 soup A, B一塊兒空的機率。
題解:咱們用 25ml 算一個 serving,先計算A, B每種 soup 的serving。咱們用一個記憶化數組來記錄算過的機率。
f[a][b] = 0.25 * (f[a-4][b] + f[a-3][b-1] + f[a-2][b-2] + f[a-1][b-3])
1 class Solution { 2 public: 3 double soupServings(int N) { 4 const int serving = N / 25 + (N % 25 >= 1 ? 1 : 0); 5 if (serving >= 500) { 6 return 1.0; 7 } 8 return helper(serving, serving); 9 } 10 double helper(int a, int b) { 11 if (a == 0 && b == 0) { 12 return 0.5; 13 } else if (a == 0) { 14 return 1.0; 15 } else if (b == 0) { 16 return 0.0; 17 } 18 if (memo.find(a) != memo.end() && memo[a].find(b) != memo[a].end()) { 19 return memo[a][b]; 20 } 21 double ret = 0.0; 22 ret = 0.25 * (helper(max(a-4, 0), b) + helper(max(a-3, 0), max(b-1, 0)) + helper(max(a-2, 0), max(b-2, 0)) + helper(max(a-1, 0), max(b-3, 0))); 23 memo[a][b] = ret; 24 return ret; 25 } 26 unordered_map<int, unordered_map<int, double>> memo; 27 };
【813】 Largest Sum of Averages (2019年5月20日,算法羣)(google tag M)
We partition a row of numbers A
into at most K
adjacent (non-empty) groups, then our score is the sum of the average of each group. What is the largest score we can achieve?
Note that our partition must use every number in A, and that scores are not necessarily integers.
Example: Input: A = [9,1,2,3,9] K = 3 Output: 20 Explanation: The best choice is to partition A into [9], [1, 2, 3], [9]. The answer is 9 + (1 + 2 + 3) / 3 + 9 = 20. We could have also partitioned A into [9, 1], [2], [3, 9], for example. That partition would lead to a score of 5 + 2 + 6 = 13, which is worse.
Note:
1 <= A.length <= 100.
1 <= A[i] <= 10000.
1 <= K <= A.length.
Answers within 10^-6 of the correct answer will be accepted as correct.
題解:dp
dp[k][i] 表示前 i 個元素劃分紅 k 的最大平均和。
dp[1][i] = avg(sum(a[0] ~ a[i-1]))
dp[k][i] = max(dp[k-1][j] + sum(a[j] ~ a[i]) / (i-j))
time complexity: O(KN^2), space complexity: O(KN)
1 //dp 和打氣球那題相似 2 class Solution { 3 public: 4 double largestSumOfAverages(vector<int>& A, int K) { 5 const int n = A.size(); 6 vector<double> _sum(n+1, 0.0); 7 for (int i = 0; i < n; ++i) { 8 _sum[i+1] = _sum[i] + A[i]; 9 } 10 vector<vector<double>> dp(K+1, vector<double>(n+1, 0.0)); 11 for (int k = 1; k <= K; ++k) { 12 for (int i = 1; i <= n; ++i) { 13 if (k == 1) { 14 dp[k][i] = _sum[i] / i; 15 } else { 16 for (int j = 1; j < i; ++j) { 17 dp[k][i] = max(dp[k][i], dp[k-1][j] + (_sum[j] - _sum[i]) / (j-i)); 18 } 19 } 20 } 21 } 22 return dp[K][n]; 23 } 24 };
【818】 Race Car
【837】 New 21 Game
【838】 Push Dominoes
【847】 Shortest Path Visiting All Nodes
【871】 Minimum Number of Refueling Stops
【873】 Length of Longest Fibonacci Subsequence
【877】 Stone Game
【879】 Profitable Schemes
【887】 Super Egg Drop