from: https://leetcode.cn/studyplan/top-100-liked/
class Solution {
public:
int climbStairs(int n) {
// int f[n + 1];
// memset(f, 0, sizeof f);
// f[0] = 1;
// f[1] = 1;
// for(int i = 2;i <= n;i ++ ){
// f[i] = f[i - 1] + f[i - 2];
// }
// return f[n];
// 写法2:优化空间写法
int a = 1, b = 1, c = 1; // debug: n=1,c 未初始化会随机一个值
for(int i = 2;i <= n;i ++ ){
c = a + b;
a = b, b = c;
}
return c;
}
};
class Solution {
public:
vector> generate(int numRows) {
vector> res;
for(int i = 0;i < numRows;i ++ ){
vector cur;
for(int j = 0;j <= i; j ++ ){
if(j == 0 || j == i) cur.push_back(1);
else{
cur.push_back(res[i - 1][j - 1] + res[i - 1][j]);
}
}
res.push_back(cur);
}
return res;
}
};
class Solution {
public:
int rob(vector& nums) {
// 线性DP:
// f[i][0]: 考虑前i个, 第i家不偷 的最大金额
// f[i][1]: 考虑前i个, 第i家偷 的最大金额
// ans = max(f[i][0/1])
// 划分依据:第i家偷不偷
// 不偷: f[i][0] = max(f[i - 1][0], f[i - 1][1])
// 偷: f[i][1] = max(f[i - 1][0] + nums[i])
int n = nums.size();
// int f[n + 1][2];
// memset(f, 0, sizeof f);
int a = 0, b = 0;
for(int i = 1;i <= n;i ++ ){
// f[i][0] = max(f[i - 1][0], f[i - 1][1]);
int temp_a = a;
a = max(a, b);
// f[i][1] = f[i - 1][0] + nums[i - 1]; // debug: nums[],下标从1开始
b = temp_a + nums[i - 1];
}
// return max(f[n][0], f[n][1]);
return max(a, b);
}
};
不需要从1开始枚举, 从j, j * j, 时间复杂度 O ( n ∗ l o g n ) O(n * logn) O(n∗logn)
class Solution {
public:
int numSquares(int n) {
// f[n] = (f[1] + f[n - 1]) + (f[2] + f[n - 2]) + ... +
// 不需要从1开始枚举, 从j, j * j
int f[n + 1];
memset(f, 0x3f, sizeof f); // debug:初始化,最少数量
f[0] = 0;
f[1] = 1;
for(int i = 1;i <= n;i ++ ){
for(int j = 1;j * j <= i;j ++ )
{
f[i] = min(f[i], f[i - j * j] + 1); // debug: 最少数量 // debug: f[i - j * j] + 1
}
}
return f[n];
}
};
class Solution {
public:
int coinChange(vector& coins, int amount) {
/* 状态表示:
集合: f[i][j]:从前i个硬币中选,且总金额恰好是j的硬币数量
属性: Min
状态计算:
f[i][j] = min(f[i-1][j],f[i-1][j - v[i]] + 1)
边界:f[i][0] = 0
*/
int n = coins.size();
vector f(amount + 1,1e9); // 初始化
f[0] = 0; // 初始化边界
for(int i=0;i= coins[i])
f[j] = min(f[j],f[j - coins[i]] + 1);
if(f[amount] == 1e9) return -1;
return f[amount];
}
};
算法1:从后往前 动态规划
时间复杂度 O ( l e n ( s ) ∗ l e n ( s ) ∗ l e n ( w o r d D i c t ) = 300 ∗ 300 ∗ 1000 ) O(len(s) * len(s) * len(wordDict) = 300 * 300 * 1000 ) O(len(s)∗len(s)∗len(wordDict)=300∗300∗1000)
字符串比较也需要O(len(s))的时间,可用 字符串哈希优化!
class Solution {
public:
bool wordBreak(string s, vector& wordDict) {
unordered_set dict(wordDict.begin(), wordDict.end()); //构建为set,方便查找
vector dp(s.size()+1, false); //dp表示字符之间的隔板,n个字符有n+1个隔板
dp[0] = true; //dp[0]是s[0]前面的隔板
for(int i=1; i<=s.size(); ++i){
for(int j=i; j>=0; --j){
if(dict.find(s.substr(j,i-j))!=dict.end() && dp[j]){
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};
算法2:从前往后 动态规划,更容易理解
动态规划 前向递推 (不一定每次都要后向思考,前向思考更简单!)+ 字符串哈希, O( n ^ 2)
class Solution {
public:
bool wordBreak(string s, vector& wordDict) {
// DP优化搜索
/*
f[i] : s[1-i] 可以拼接
f[i] = f[k - 1] + wordDict.count(s[k ~ i]);
如果是哈希表直接判断字符串,也要计算上时间复杂度O(L),L单词长度
考虑字符串哈希优化
*/
typedef unsigned long long ULL;
const int P = 131;
unordered_set S;
for(auto &c : wordDict)
{
ULL h = 0;
for(auto d : c) h = h * P + d; // h 是 乘P
S.insert(h);
}
int n = s.size();
s = ' ' + s;
vector f(n + 1);
f[0] = true;
for(int i = 0;i < n;i ++ )
{
if(f[i])
{
ULL h = 0;
for(int j = i + 1;j <= n;j ++ ) // 因为要计算h,所以这里枚举 能扩展到j
{
h = h * P + s[j];
if(S.count(h)) f[j] = true;
}
}
}
return f[n];
}
};
算法1:常规动态规划, O ( n 2 ) O(n^2) O(n2)
class Solution {
public:
int lengthOfLIS(vector& nums) {
// O(n ^ 2)
int n = nums.size();
vector f(n);
int ans = 0;
for(int i = 0;i < n;i ++ ){
f[i] = 1;
for(int j = 0;j < i;j ++ ){
if(nums[i] > nums[j]) f[i] = max(f[i], f[j] + 1);
}
ans = max(ans, f[i]);
}
return ans;
}
};
算法2:贪心 + 二分, O ( n ∗ l o g n ) O(n * logn) O(n∗logn)
q[i] : 长度为i 的 最小值 q[i]
class Solution {
public:
int lengthOfLIS(vector& nums) {
// O(n ^ 2)
// 优化:核心:在 q(n + 1);
int len = 0;
for(int i = 0;i < n;i ++ ){
int l = 0, r = len;
while(l < r){
int mid = l + r + 1 >> 1;
if(q[mid] < nums[i]) l = mid;
else r = mid - 1;
}
q[r + 1] = nums[i];
len = max(len, r + 1);
}
return len;
}
};
class Solution {
public:
int maxProduct(vector& nums) {
// 动态规划
// 维护最大和最小
/*
f[i] : 以i结尾,乘积最大的非空连续子数组
g[i] : 以i结尾,乘积最小的非空连续子数组
f[i] = max(f[i - 1] * nums[i], g[i - 1] * nums[i]);
g[i] = min(f[i - 1] * nums[i], g[i - 1] * nums[i]);
ans = max(f[i], g[i])
*/
int n = nums.size();
int f[n], g[n];
memset(f, -0x3f, sizeof f);
memset(g, 0x3f, sizeof g);
int ans;
f[0] = g[0] = ans = nums[0];
for(int i = 1;i < n;i ++ ){
f[i] = max({f[i - 1] * nums[i], g[i - 1] * nums[i], nums[i]});
g[i] = min({f[i - 1] * nums[i], g[i - 1] * nums[i], nums[i]});
ans = max(f[i], ans); // debug: ans = max(f[i], ans);
}
return ans;
}
};
class Solution {
public:
bool canPartition(vector& nums) {
// 子集元素和 = sum / 2
// 也就是 从 nums中挑选 和为 sum / 2
int s = 0;
for(auto c : nums) s += c;
if(s & 1) return false;
// f[i][j] 考虑前i个元素,和为j, O(n * s) = O(200 * 20000)
// 不选i: f[i][j] = f[i - 1][j]
// 选i: f[i][j] = f[i - 1][j - nums[i]]
// 空间优化
// f[j] = f[j - nums[i]] ==> debug: 因为 j - nums[i] < j 是先遍历的,所以是第i层的,
// 不符合 f[i - 1][j - nums[i]], 需要倒着遍历!!!
// 先写好状态方程,再考虑代码优化!
s = s / 2;
int n = nums.size();
bool f[s + 1];
memset(f, 0, sizeof f);
f[0] = 1;
for(int i = 1; i <= n;i ++ ){
// for(int j = 0;j <= s;j ++ ){
for(int j = s;j >= 0 ;j -- ){ // debug: j 倒着遍历
if(j >= nums[i - 1]) f[j] |= f[j - nums[i - 1]];
}
}
return f[s];
}
};
class Solution {
public:
int longestValidParentheses(string s) {
stack stk;
int res = 0;
for(int i = 0, start = -1; i < s.size(); i ++ )
{
if(s[i] == '(') stk.push(i);
else if(s[i] == ')')
{
if(stk.size())
{
stk.pop();
if(stk.size()) res = max(res, i - stk.top());
else res = max(res, i - start);
}else{
start = i; // 不合法,直接 start 移到i
}
}
}
return res;
}
};