312. Burst Balloons
Given n
balloons, indexed from 0
to n-1
. Each balloon is painted with a number on it represented by array nums
. You are asked to burst all the balloons. If the you burst balloon i
you will get nums[left] * nums[i] * nums[right]
coins. Here left
and right
are adjacent indices of i
. After the burst, the left
and right
then becomes adjacent.
Find the maximum coins you can collect by bursting the balloons wisely.
Note:
(1) You may imagine nums[-1] = nums[n] = 1
. They are not real therefore you can not burst them.
(2) 0 ≤ n
≤ 500, 0 ≤ nums[i]
≤ 100
思路:
我们维护一个二维动态数组dp,其中dp[i][j]表示打爆区间[i,j]中的所有气球能得到的最多金币。题目中说明了边界情况,当气球周围没有气球的时候,旁边的数字按1算,这样我们可以在原数组两边各填充一个1,这样方便于计算。【即新建的vector的大小是n+2维! 如果还是n维 就会超时】
递推式为:dp[i][j] = max(dp[i][j], dp[i][k - 1] + nums[i - 1]*nums[k]*nums[j + 1] + dp[k + 1][j]) ( i ≤ k ≤ j )
含义:打破i~j间气球所得金币 = 之前的or打破i~k-1间气球所得金币 + 打破k+1~j间气球所得金币
+ nums[i - 1]*nums[k]*nums[j + 1] (这次打破k的:因为i~k-1破了,所以他的左边相邻是nums[i-1],同理,右边相邻是nums[j+1])
代码:
注意点:1.新建的dp数组必须是n+2维;2.left的范围是1~(n-打破长度+1),而不是1~n【会超时】;3.return的是dp[1][n]【本应是dp[0][n-1],但因为前面加了边界的1,所以~】
class Solution {
public:
int maxCoins(vector& nums) {
int n = nums.size();
if(n == 0) return 0;
nums.insert(nums.begin(), 1);
nums.insert(nums.end(), 1);
vector > dp (n + 2, vector(n + 2, 0));
for(int len = 1; len <= n; ++len) {
for(int left = 1; left <= n - len + 1; ++left) {
int right = left + len - 1;
for(int k = left; k <= right; ++k) {
dp[left][right] = max(dp[left][right], dp[left][k-1] + nums[left-1]*nums[k]*nums[right+1] + dp[k + 1][right]);
}
}
}
return dp[1][n];
}
};
322. Coin Change
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1
.
Example 1:
coins = [1, 2, 5]
, amount = 11
return 3
(11 = 5 + 5 + 1)
Example 2:
coins = [2]
, amount = 3
return -1
.
Note:
You may assume that you have an infinite number of each kind of coin.
思路:
dp[i][v], which is the minimum number of coins used to sum up to v, and i is the number of different denominations used for this sum (use the first i denominations in coins).
f[i][v] = min {case1, case2}
Case1 is f[i-1][v], where coins[i] isn't used;
Case2 is f[i][v-coins[i-1]]+1, where coins[i] is used (and can be used multiple times)
So the weight-lifting work is now finished, and all we have to do is to iterate i from 1 to coins.size() (set count[0] to 0, obviously), while for each i we update coins[v] from v=cost[i-1] to v=amount (no need to start from v=0, because count[v-coins[i-1]] is meaningful only when v-coins[i-1]>0
After finishing all this, return count[amount] as the final result (if count[amouint] is still INT_MAX, which means the search fails, then return -1)
class Solution {
public:
int coinChange(vector& coins, int amount) {
vector dp(amount + 1, amount + 1);//初值amount+1,表达INT_MAX的功能,若用后者会有潜在风险,因为后文会用到该值+1,则变成0x80000000,最小值了
dp[0] = 0;
for(int i = 0; i < coins.size(); ++i) {
for(int v = coins[i]; v <= amount; ++v)
dp[v] = min(dp[v], dp[v - coins[i]] + 1);
}
return (dp[amount] == amount + 1)? -1: dp[amount];
}
};
338. Counting Bits
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.
Example:
For num = 5
you should return [0,1,1,2,1,2]
.
vector countBits(int num) {
vector ret(num + 1, 0);
for(int i = 1; i <= num; ++i)
ret[i] = ret[i>>1] + i % 2;
return ret;
}
343. Integer Break
Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those integers. Return the maximum product you can get.
For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4).
Note: You may assume that n is not less than 2 and not larger than 58.
Hint:
int integerBreak(int n) {
if (n == 2 || n == 3)
return n - 1;
int res = 1;
while (n > 4) {
res *= 3;
n -= 3;
}
return res * n;
}
357. Count Numbers with Unique Digits
Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.
Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99]
)
思路:
令 f(n) 为所求结果。
f(1) = 10. (0, 1, 2, 3, ...., 9)
f(2) = 9 * 9. Because for each number i from 1, ..., 9, we can pick j to form a 2-digit number ij and there are 9 numbers that are different from i for j to choose from.
f(3) = f(2) * 8 = 9 * 9 * 8. Because for each number with unique digits of length 2, say ij, we can pick k to form a 3 digit number ijk and there are 8 numbers that are different from i and j for k to choose from.
Similarly f(4) = f(3) * 7 = 9 * 9 * 8 * 7....
...
f(10) = 9 * 9 * 8 * 7 * 6 * ... * 1
f(11) = 0 = f(12) = f(13)....
Hence return f(1) + f(2) + .. + f(n)
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if(n == 0) return 1;
int result = 10;
int uniqueNumber = 9;
int availbleNumber = 9;//即f()
for(int i = 1; i < n; ++i) {
availbleNumber *= uniqueNumber;
result += availbleNumber;
uniqueNumber--;
if(uniqueNumber <= 0)
break;
}
return result;
}
};
376. Wiggle Subsequence
A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.
For example, [1,7,4,9,2,5]
is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast,[1,4,7,2,5]
and [1,7,4,5,5]
are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.
Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.
Examples:
Input: [1,7,4,9,2,5] Output: 6 The entire sequence is a wiggle sequence. Input: [1,17,5,10,13,15,10,5,16,8] Output: 7 There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. Input: [1,2,3,4,5,6,7,8,9] Output: 2题意:找出这样一个序列,其相邻元素的差值 交替正负(0不算),该序列不要求连续。
代码:(贪婪思想)
class Solution {
public:
int wiggleMaxLength(vector& nums) {
int n = nums.size();
if(n < 2) return n;
int flag = 0;
int count = 1;
for(int i = 1; i < n; ++i) {
if(nums[i] < nums[i -1] && (flag == 0 || flag == 1)) {
flag = -1;
++count;
}
if(nums[i] > nums[i - 1] && (flag == 0 || flag == -1)) {
flag = 1;
++count;
}
}
return count;
}
};
96. Unique Binary Search Trees
Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,
Given n = 3, there are a total of 5 unique BST's.
1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3本题使用一维线性规划解决。
如果n == 0时,结果为0;
如果n == 1时,只有一个节点,结果为1;
如果n == 2时,根节点有两种选择,结果为2;
如果n >= 3时,n个点中每个点都可以作为root,当 i 作为root时,小于 i 的点都只能放在其左子树中,大于 i 的点只能放在右子树中,此时只需求出左、右子树各有多少种,二者相乘即为以 i 作为root时BST的总数。
class Solution {
public:
int numTrees(int n) {
if(n <= 2)
return n;
vector dp(n + 1, 0);
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; ++i) {
int tmp = 0;
for(int j = 0; j < i; ++j) {
tmp += dp[j] * dp[ i - j - 1];
}
dp[i] = tmp;
}
return dp[n];
}
};
62. Unique Paths
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
How many possible unique paths are there?
一、题目描述:
给定一个m*n的矩阵,让机器人从左上方走到右下方,只能往下和往右走,一共多少种走法。
二、解题方法:
//动态规划:
//设状态为f[i][j],表示从起点(1;1)到达(i; j)的路线条数,则状态转移方程为:
//f[i][j] = f[i-1][j] + f[i][j-1]
class Solution {
public:
int uniquePaths(int m, int n) {
vector > f(m, vector(n, 1));
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
f[i][j] = f[i - 1][j] + f[i][j - 1];
return f[m - 1][n - 1];
}
};
上面方法的空间复杂度较大为O(m*n),然而通过观察可以发现,我们每次更新f[i][j]只需要f[i-1][j](同一列)和f[i][j-1](左一列),所以只要保存当前列和左一列就行,而不是整个m*n矩阵,下面的代码可以将空间复杂度优化到O(min(m,n))
class Solution {
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m);
vector pre(m, 1);
vector cur(m, 1);
for (int j = 1; j < n; j++) {
for (int i = 1; i < m; i++)
cur[i] = cur[i - 1] + pre[i];
swap(pre, cur);
}
return pre[m - 1];
}
};
通过进一步的观察,我们还可以发现,上面程序中的pre[i]就是更新前的cur[i],所以可以进一步优化为:
class Solution {
int uniquePaths(int m, int n) {
if (m > n) return uniquePaths(n, m);
vector cur(m, 1);
for (int j = 1; j < n; j++)
for (int i = 1; i < m; i++)
cur[i] += cur[i - 1];
return cur[m - 1];
}
};
最终优化空间程序为:(其实先遍历m还是先遍历n都无所谓的。关键是要注意 vector的长度 与 内层for循环的长度 是一样的~!)
class Solution{
public:
int uniquePaths(int m, int n) {
if (m == 0 && n == 0)
return 0;
vector dp(n, 1);
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
dp[j] = dp[j - 1] + dp[j];
return dp[n - 1];
}
};
63. Unique Paths II
Follow up for "Unique Paths":
Now consider if some obstacles are added to the grids. How many unique paths would there be?
An obstacle and empty space is marked as 1
and 0
respectively in the grid.
For example,
There is one obstacle in the middle of a 3x3 grid as illustrated below.
[ [0,0,0], [0,1,0], [0,0,0] ]
The total number of unique paths is 2
.
Note: m and n will be at most 100.
这道题跟 Unique Paths 差不多,只是这道题给机器人加了障碍,不是每次都有两个选择(向右,向下)了。class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
if(m == 0 || n ==0)
return 0;
vector dp(n);
dp[0] = 1;
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
if(obstacleGrid[i][j] == 1)
dp[j] = 0;
else if(j > 0)
dp[j] += dp[j - 1];
}
}
return dp[n - 1];
}
};
64. Minimum Path Sum
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
这是动态规划的问题,由于每次只能向下或者向右移动,因此[i, j]位置时的最小路径的和等于[i, j-1] 与 [i-1, j]中较小的加上[i, j]位置的数值。
因此递推公式是grid[i][j] += min(grid[i][j-1], grid[i-1][j])。
时间复杂度:O(mn)
class Solution {
public:
int minPathSum(vector>& grid) {
int m = grid.size();
int n = grid[0].size();
if(m == 0 && n == 0)
return 0;
vector > dp (m, vector(n, 0));
dp[0][0] = grid[0][0];
for(int i = 1; i < m; ++i)
dp[i][0] += grid[i][0] + dp[i - 1][0];
for(int i = 1; i < n; ++i)
dp[0][i] += grid[0][i] + dp[0][i - 1];
for(int i = 1; i < m; ++i) {
for(int j = 1; j < n; ++j) {
dp[i][j] += grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m - 1][n - 1];
}
};
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...
) which sum to n.
For example, given n = 12
, return 3
because 12 = 4 + 4 + 4
; given n = 13
, return 2
because 13 = 4 + 9
.
用动态规划Dynamic Programming来做,我们建立一个长度为n+1的一维dp数组,将第一个值初始化为0,其余值都初始化为INT_MAX.i从0循环到n,j从1循环到i+j*j <= n的位置,然后每次更新dp[i+j*j]的值,动态更新dp数组,其中dp[i]表示正整数i能少能由多个完全平方数组成,那么我们求n,就是返回dp[n]即可,也就是dp数组的最后一个数字,参见代码如下:
class Solution {
public:
int numSquares(int n) {
vector dp(n + 1, 0x7fffffff);
for(int i = 0; i * i <= n; ++i)
dp[i * i] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 1; i + j * j <= n; ++j) {
dp[i + j * j] = min(dp[i] + 1, dp[i + j * j]);
}
}
return dp[n];
}
};
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.
求一个三角形二维数组从顶到低端的最小路径和。每次只能挪一个位置。
我们从低端向顶端计算。设状态为 S[i][j]表示从从位置 ( i, j ) 出发,到最低端路径的最小和
状态转移方程:S[i][j] = min(S[i+1][j] + S[i+1][j+1]) +S[i][j]
S[0][0]就是要求解的答案。
时间复杂度 O(n^2) ,空间复杂度 O(1)
class Solution {
public:
int minimumTotal(vector > &triangle) {
int size = triangle.size();
// down-to-top
// 第i层
for(int i = size - 2;i >= 0;--i){
// 第i层的第j个元素
for(int j = 0;j <= i;++j){
triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]);
}
}
return triangle[0][0];
}
};
从上面思路的状态转移方程中看出:S[i][j] = min(S[i+1][j] + S[i+1][j+1]) +S[i][j]
S[i][j]只与下一行的第j个元素和第j+1个元素相关,i的关系是固定的,因此我们可以省去这一维。
开辟O(N)的数组,然后规划的时候使用S[j] = min(S[j+1], S[j) +Triangle[i][j]就可以了。
class Solution {
public:
int minimumTotal(vector > &triangle) {
int n = triangle.size();
vector dp(triangle.back());//dp初值设为triangle的最后一行
// down-to-top
// 第i层
for(int i = n - 2;i >= 0;--i){
// 第i层的第j个元素
for(int j = 0;j <= i;++j){
dp[j] = min(dp[j], dp[j+1]) + triangle[i][j];
}
}
return dp[0];
}
};
91. Decode Ways
A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1 'B' -> 2 ... 'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,
Given encoded message "12"
, it could be decoded as "AB"
(1 2) or "L"
(12).
The number of ways decoding "12"
is 2.
思路:每次对于当前的字符判断是否属于1-9(0肯定不行,因为0不在1-26中),如果属于,那么当前的字符可以被decode,并且和f[n-1]组合,f[n] += f[n-1]
然后对于当前字符和前一个字符组成的字符串判断是否属于10-26,如果属于,那么这两个字符可以被decode,并且和f[n-2]组合,f[n] += f[n-2]
而result[1]初始化时不要出错了,它 = (check(s[0]) & check(s[1])) + check(s[0], s[1]);
class Solution {
public:
int checkOne(char a){
return (a == '0') ? 0: 1;
}
int checkTwo(char a, char b){
return (a == '1' || a == '2' && b>= '0' && b <= '6')? 1: 0;
}
int numDecodings(string s) {
int length = s.size();
if(length == 0) return 0;
if(length == 1) return checkOne(s[0]);
vector result(length + 1, 0);
result[0] = checkOne(s[0]);
result[1] = checkTwo(s[0], s[1]) + (checkOne(s[0]) & checkOne(s[1]));
for(int i = 2; i < length; ++i) {
if(checkOne(s[i]))
result[i] += result[i - 1];
if(checkTwo(s[i - 1], s[i]))
result[i] += result[i - 2];
}
return result[length - 1];
}
};
至此可见,有点像斐波那契数列,只需记录下“上一个”和“上上个”的结果即可,无需O(n)空间。不再赘附代码。
72. Edit Distance 编程之美里的“字符串相似度”
Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character
dp[i][j]指把word1[0..i - 1]转换为word2[0..j - 1] 的最小操作数。
边界条件:
dp[i][0] = i; 从长度为 i 的字符串转为空串 要删除 i 次
dp[0][j] = j. 从空串转为长度为 j 的字符串 要添加 j 次
一般情况:
如果word[i - 1] == word2[j - 1],则dp[i][j] = dp[i - 1][j - 1],因为不需要进行操作,即操作数为0.
如果word[i - 1] != word2[j - 1],则需考虑三种情况,取最小值:
Replace word1[i - 1] by word2[j - 1]: (dp[i][j] = dp[i - 1][j - 1] + 1 (for replacement));
Delete word1[i - 1]: (dp[i][j] = dp[i - 1][j] + 1 (for deletion));
Insert word2[j - 1] to word1[0..i - 1]: (dp[i][j] = dp[i][j - 1] + 1 (for insertion)).
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
vector > dp(m + 1, vector(n + 1, 0));
//边界条件
for(int i = 1; i <= m; ++i) //从长度为i的字符串转为空串 要删除i次~
dp[i][0] = i;
for(int j = 1; j <= n; ++j)//从空串转为长度为j的字符串 要添加j次~
dp[0][j] = j;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
if(word1[i - 1] == word2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
}
}
return dp[m][n];
}
};
可以发现,当我们更新dp[i][j]时,我们只需要dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]。所以,我们不必记录整个m*n矩阵。事实上,我们只维护一列就够了。空间复杂度可以被优化为O(m) 【维护一列】或 O(n)【维护一行】。
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length();
vector cur(m + 1, 0);
for (int i = 1; i <= m; i++)
cur[i] = i;
for (int j = 1; j <= n; j++) {
int pre = cur[0];
cur[0] = j;
for (int i = 1; i <= m; i++) {
int temp = cur[i];
if (word1[i - 1] == word2[j - 1])
cur[i] = pre;
else cur[i] = min(pre + 1, min(cur[i] + 1, cur[i - 1] + 1));
pre = temp;
}
}
return cur[m];
}
};
f[i][j] only depends on f[i-1][j-1], f[i-1][j] and f[i][j-1], we can reduce the space to O(n) by using only the (i-1)th array and previous updated element(f [i] [j - 1]).【上法是用一列,所以for循环先n后m。此处用一行,for循环就先m后n,即内层个数 = vector长度】
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
vector dp(n+1, 0);
for (int j = 1; j <= n; ++j)
dp[j] = j;
for (int i = 1; i <= m; ++i) {
int prev = i;
for (int j = 1; j <= n; ++j) {
int cur;
if (word1[i-1] == word2[j-1])
cur = dp[j-1];
else
cur = min(min(dp[j-1], prev), dp[j]) + 1;
dp[j-1] = prev;
prev = cur;
}
dp[n] = prev;
}
return dp[n];
}
115. Distinct Subsequences
Given a string S and a string T, count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE"
is a subsequence of "ABCDE"
while "AEC"
is not).
Here is an example:
S = "rabbbit"
, T = "rabbit"
Return 3
.
思路:
我们维护dp[i][j],对应的值是S的前i个字符和T的前j个字符有多少个可行的序列(注意这道题是序列,不是子串,也就是只要字符按照顺序出现即可,不需连续出现)。下面来看看递推式,假设我们现在拥有之前的历史信息,我们怎么在常量操作时间内得到dp[i][j]。
假设S的第i个字符和T的第j个字符不相同,那么就意味着dp[i][j]的值跟dp[i-1][j]是一样的,前面该是多少还是多少,而第i个字符的加入也不会多出来任何可行结果。如果S的第i个字符和T的第j个字符相同,那么所有dp[i-1][j-1]中满足的结果都会成为新的满足的序列,当然dp[i-1][j]的也仍是可行结果,所以dp[i][j] = [i-1][j-1] + dp[i-1][j]。
例子:
不同时:S = ABCDE, T = F。此时S[6] != T[1], dp[5][1] = dp[4][1]【即S变成ABCD,少了一个】
相同时:S = ABCDBB, T = AB。 此时S[6] == T[2]。
dp[6][2]【意为S的前6个里面找T的前2个的次数】 = dp[5][1] 【T中的B是S中的最后一个B,即就是这个二者相等的B。那么问题就变成了S = ABCDB, T = A,即dp[5][1]】 + dp[5][2]【T中的B不是S中的最后一个B,问题就变成了S = ABCDB, T = AB,即dp[5][2]】
所以综合上面两种情况,递推式应该是dp[i][j]=(S[i]==T[j]?dp[i-1][j-1]:0)+dp[i][j]。算法进行两层循环,时间复杂度是O(m*n)
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size(), n = t.size();
if(m == 0 || m < n)
return 0;
vector > dp(m + 1, vector(n + 1, 0));
for(int i = 0; i < m; ++i)//从任意长度的字符串转化为空串,只有全部删除这一种方法
dp[i][0] = 1;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
if(s[i - 1] == t[j - 1])
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[m][n];
}
};
发现每次更新只跟[i-1]行有关,所以可以优化空间复杂度。
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size(), n = t.size();
if(m == 0 || m < n)
return 0;
vector dp(n + 1, 0);
dp[0] = 1;
for(int i = 1; i <= m; ++i) {
int pre = 1;
for(int j = 1; j <= n; ++j) {
int tmp = dp[j];
dp[j] = dp[j] + (s[i-1] == t[j-1] ? pre: 0);//+比 三目运算符优先级别要高!!!! 所以要把整个的三目运算符用括号括起来
pre = tmp;
}
}
return dp[n];
}
};
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.
For example, given s = "aab"
,
Return
[ ["aa","b"], ["a","a","b"] ]题意:给出一个string s,把它分割成一些子串,要求每个子串都是回文的。求所有可能。
思路:DFS,去找所有可能,有点类似剑指offer 28 字符串的全排列,只是多了一个判断条件:是否为回文。
步骤:从左到右遍历字符串,当传入起点为index时,判断s(index,i)作为一个子串是否回文。若是回文,将其保存至tmp中,接着调用dfs函数继续处理后面的部分s(i+1, ...)。当遍历至结尾,将tmp保存到result中,作为一种情况。
class Solution {
public:
bool IsPalindrome(const string s, int start, int end) {
while(start < end) {
if(s[start] != s[end])
return false;
start++, end--;
}
return true;
}
void dfs(int index, string s, vector& tmp, vector >& result) {
if(index == s.size()) {
result.push_back(tmp);
return;
}
for(int i = index; i < s.size(); ++i) {
if(IsPalindrome(s, index, i)) {
tmp.push_back(s.substr(index, i - index + 1));//substr的两个参数:起点,长度
dfs(i + 1, s, tmp, result);
tmp.pop_back();
}
}
}
vector> partition(string s) {
vector > result;
if(!s.empty()) {
vector tmp;
dfs(0, s, tmp, result);
}
return result;
}
};
Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s = "aab"
,
Return 1
since the palindrome partitioning ["aa","b"]
could be produced using 1 cut.
class Solution {
public:
int minCut(string s) {
int n = s.size();
if(n <= 1) return 0;
vector minCut(n + 1);//minCut[i]代表前i个字符(0~i-1)需要的最少分割次数。
for(int i = 0; i <= n; ++i)
minCut[i] = i - 1;//赋初值:前i个字符最坏情况下是分割i-1次。
for(int i = 1; i < n; ++i) {//循环,表示以i为中心,j为对称长度的回文串
//奇数长度回文串abcdcbe 此时i是d,画图 即懂
for(int j = 0; i - j >= 0 && i + j < n && s[i - j] == s[i + j]; ++j)
minCut[i + j + 1] = min(minCut[i + j + 1], minCut[i - j] + 1);
//偶数长度回文串abcddcbe 此时的i是第二个d,画图,即懂
for(int j = 0; i - j - 1 >= 0 && i + j < n && s[i - j - 1] == s[i + j]; ++j)
minCut[i + j + 1] = min(minCut[i + j + 1], minCut[i - j - 1] + 1);
//当内部两个j的循环结束,说明以i为对称中心的处理完了,接着处理下一个
}
return minCut[n];
}
};