357. Count Numbers with Unique Digits
解题思路:
用arr[i]存放长度为i时,各位互不相同的数字的个数,所以arr[1]=10,arr[2]=9*9。(第一位要为1,第二位与第一位要不同)
arr[3] = arr[2]*8,所以arr[i]=arr[i-1]*(10 - (k-1))。之后求和就可以了。
int countNumbersWithUniqueDigits(int n) { if (!n) return 1; if (n == 1) return 10; if (n == 2) return 91; int sum = 0; int arr[n + 2]; arr[1] = 10; arr[2] = 81; for (int i = 3; i <= n; i++) arr[i] = arr[i-1] * (11 - i); for (int i = 1; i <= n; i++) { sum += arr[i]; } return sum; }
5. Longest Palindromic Substring
解题思路:
这道题使用一个数组dp[i][j]存储子串s[i...j]是否为回文串。那么dp[i][i]=true(i = 0...n), dp[i][i-1]=true(i = 1...n)
其他为false。判断的时候,dp[i][j] = (s[i] == s[j] && dp[i+1][j-1] == true)。同时,需要记录最长回文串的位置。
枚举子串时,从k=2开始到n。
string longestPalindrome(string s) { if (s.length() < 2) return s; int left = 0; int right = 0; bool dp[s.length()][s.length()]; memset(dp, false, sizeof(dp)); dp[0][0] = true; for (int i = 1; i < s.length(); i++) { dp[i][i] = true; dp[i][i-1] = true; } int i, j, k; for (k = 2; k <= s.length(); k++) { for (i = 0; i <= s.length() - k; i++) { j = i - 1 + k; if (s[i] == s[j] && dp[i+1][j-1] == true) { dp[i][j] = true; if (right - left + 1 < k) { left = i; right = j; } } } } return s.substr(left, right - left + 1); }
516. Longest Palindromic Subsequence
解题思路:
subsequence与substring的区别在于它可以是不连续的,此处的思路是:
用dp[i][j]存储子序列s[i...j]中最长回文串的长度。初始化时,除了dp[i][i]=1外,其它都置为0。
枚举子序列的长度,从2开始。所以如果s[i]==s[j],那么dp[i][j] = dp[i+1][j-1]+2;否则
dp[i][j] = max(dp[i-1][j], dp[i][j-1])。最后只要返回dp[0][n-1]就可以了。
int longestPalindromeSubseq(string s) { if (s.size() < 2) return s.size(); int dp[s.size()][s.size()]; for (int i = 0; i < s.size(); i++) { for (int j = 0; j < s.size(); j++) { dp[i][j] = 0; } dp[i][i] = 1; } int i, j, k; for (k = 1; k < s.size(); k++) { for (i = 0; i < s.size() - k; i++) { j = i + k; if (s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2; else dp[i][j] = dp[i+1][j] > dp[i][j-1] ? dp[i+1][j] : dp[i][j-1]; } } return dp[0][s.size()-1]; }
368. Largest Divisible Subset
解题思路:
这道题类似于求最长递增子序列。。考虑的是,大的数整除小的数,所以现将数组排序。然后用dp[i]记录以第i个数结尾的最长可整除子集的长度。
那么状态转移方程为:dp[i] = max{dp[j] + 1},j = 0...i-1。同时,要求dp[j]%dp[i]==0。另外,因为需要记录子集的内容,所以使用另一个
数组set来保留加入的序号(针对nums的),使用max记录最大长度,last记录最后一个序号。
需要注意的是,C++中数组的赋值,不能用int dp[n] = {1},因为这样只将dp[0]赋值为1,其他为0;也不能用memset,很奇怪==
vector<int> largestDivisibleSubset(vector<int>& nums) { if (nums.size() < 2) return nums; // sort sort(nums.begin(), nums.end()); // this way, only get 1,0,0 //int dp[nums.size()] = {1}; int dp[nums.size()]; // wrong! //memset(dp, 1, nums.size()); int set[nums.size()]; for (int i = 0; i < nums.size(); i++) { dp[i] = 1; set[i] = -1; } int max = 0; int last = -1; vector<int> result; for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] % nums[j] == 0 && dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; set[i] = j; } if (dp[i] > max) { max = dp[i]; last = i; } } } // get result for (int i = last; i >= 0;) { result.insert(result.begin(), nums[i]); i = set[i]; } return result; }
494. Target Sum
解题思路:
可以将所有数字分为两个组,positive和negative,那么
positive - negative = target
positive + negative = sum
所以两式相加,2postive = target + sum。所以可以把所有数字变为原来的两倍,选其中一部分数字作为positive,剩下的自然是negative,
看有多少种选法可以使得总和为target+sum。因此使用数组dp[i]来记录总和达到i的方法数。注意:
1) 初始化dp[0] = 1
2) 只考虑j>=nums[i]的情况,而且j要从target开始,不能从0开始,否则会WA
int findTargetSumWays(vector<int>& nums, int S) { int sum = 0; for(int i = 0; i < nums.size(); i++) { sum += nums[i]; nums[i] *= 2; } if (sum < S) return 0; int target = sum + S; int dp[target + 1]; // initialize dp[0] = 1; for (int i = 0; i < nums.size(); i++) { // ATTENTION: j for (int j = target; j >= 0; j--) { if (j >= nums[i]) { dp[j] += dp[j - nums[i]]; } } } return dp[target]; }
343. Integer Break
解题思路:
首先n=2时返回1,n=3时返回2,这两个需要特别考虑。然后用result[i]存储和为i时乘积最大值,因此
result[i]=max{result[i-3]*3, result[i-2]*2}。而关于result[2]和result[3]的值,需要观察。。
i = 4, max(1*3, 2*2) = 4
i = 5, max(result[2]*3, result[3]*2) = max(2*3, 3*2) = 6
i = 6, max(result[3]*3, result[4]*2) = max(3*3, 4*2) = 9
因此,result[2] = 2, result[3] = 3
int integerBreak(int n) { int result[n+1] = {0}; if (n <= 3) return n-1; result[2] = 2; result[3] = 3; for (int i = 4; i <= n; i++) { result[i] = 3 * result[i-3] > 2 * result[i-2] ? 3 * result[i-3] : 2 * result[i-2]; } return result[n]; }
486. Predict the Winner
https://leetcode.com/problems/predict-the-winner/#/description
简单的说就是两个人轮流抽牌,每个人都可以从头抽或者从尾抽,抽到了就加相应的分数,最后看谁的分高。
解题思路:
用了递归。。
bool PredictTheWinner(vector<int>& nums) { return myFunc(nums, 0, nums.size()-1) >= 0; } int myFunc(vector<int>& nums, int start, int end) { if (start == end) return nums[start]; int i = nums[start] - myFunc(nums, start+1, end); int j = nums[end] - myFunc(nums, start, end-1); return i > j ? i : j; }