关于 动态规划的理解 与例题,点击👇
有了上面的经验,我们来解下面 子数组问题 :
下面的例题帮助我们理解子数组类问题的解法:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n + 1); // 创建dp数组
// dp[0] = 0; 默认为0
int ret = INT_MIN;
for(int i = 1; i <= n; ++i)
{
dp[i] = max(nums[i - 1], dp[i-1] + nums[i - 1]);
ret = max(ret, dp[i]);
}
return ret;
}
};
思路
有了上面两种状态,自然我们可以创建两个dp数组,下面进行状态表示设置:
随后进行 内容初始化以及其他细节:
代码
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n = nums.size();
// 创建dp数组
vector<int> f(n + 1); // 记录到i位置时的子数组最大和
auto g = f; // 记录到i位置时的子数组最小和
// dp数组的计算
int fmax = INT_MIN, gmin = INT_MAX, sum = 0;
for(int i = 1; i <= n; ++i)
{
int x = nums[i - 1];
f[i] = max(x, f[i-1] + x);
fmax = max(fmax, f[i]); // f最大和
g[i] = min(x, g[i-1] + x);
gmin = min(gmin, g[i-1]); // g最小和
sum += x; // 统计数组总和
}
// 当nums全部为负数时,sum - g[i]为0,而子数组最少要选择一位
return sum == gmin ? fmax : max(fmax, sum - gmin);
}
};
思路
代码
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size();
vector<int> f(n+1); // 以i位置为结尾时,数组的最大乘积
auto g = f; // 以i位置为结尾时,数组的最小乘积
f[0] = g[0] = 1; // 初始化
int ret = INT_MIN;
for(int i = 1; i <= n; ++i)
{
int x = nums[i-1], y = f[i-1] * nums[i - 1], z = g[i-1] * nums[i - 1];
f[i] = max(max(x, y), z);
g[i] = min(min(x, y), z);
ret = max(ret, f[i]);
}
return ret;
}
};
思路
代码
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n = nums.size();
vector<int>f(n + 1), g(n + 1); // 创建dp数组
// 默认初始化为f[0] = g[0] = 0;
int ret = 0;
for(int i = 1; i <= n; ++i)
{
if(nums[i - 1] > 0) {
f[i] = f[i-1] + 1;
g[i] = g[i-1] == 0 ? 0 : g[i-1] + 1;
} else if(nums[i - 1] < 0) {
f[i] = g[i-1] == 0 ? 0 : g[i-1] + 1;
g[i] = f[i-1] + 1;
}
ret = max(ret, f[i]);
}
return ret;
}
};
思路
代码
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n); // dp[i]: 以i为结尾的子数组中等差数列的个数
// dp[0] = dp[1] = 0; // 默认为0,无需初始化
int sum = 0; // 记录等差数列的个数
for(int i = 2; i < n; ++i)
{
dp[i] = nums[i] - nums[i-1] == nums[i-1] - nums[i-2] ? dp[i-1] + 1 : 0;
sum += dp[i];
}
return sum;
}
};
思路
代码
class Solution {
public:
int maxTurbulenceSize(vector<int>& arr) {
int n = arr.size();
// 创建dp数组 + 初始化
// 以i为结尾的所有子数组中,为上升 / 下降状态的最长长度
vector<int> f(n, 1), g(n, 1);
int ret = 1;
for(int i = 1; i < n; ++i)
{
if(arr[i - 1] < arr[i]) f[i] = g[i-1] + 1;
else if(arr[i-1] > arr[i]) g[i] = f[i-1] + 1;
ret = max(ret, max(f[i], g[i]));
}
return ret;
}
};
思路
代码
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
// 优化:利用哈希表存储数组中的单词
unordered_set<string> hash;
for(auto& word : wordDict)
hash.insert(word);
int n = s.size();
vector<bool> dp(n + 1, false); // dp[i] 表示前 i 个字符能否被拆分
dp[0] = true; // 空字符串可以被拆分
for (int i = 1; i <= n; ++i)
for (int j = i - 1; j >= 0; --j)
{
if (dp[j] && hash.count(s.substr(j, i - j)))
{
dp[i] = true;
break;
}
}
return dp[n];
}
};
思路
题意分析
代码
class Solution {
public:
int findSubstringInWraproundString(string s) {
int n = s.size();
// 创建dp数组 + 初始化
vector<int> dp(n, 1); // dp[i]: 以i为结尾的所有子数组中,在base里的数组个数
// 填数组
for(int i = 1; i < n; ++i)
if(s[i-1]+1 == s[i] || (s[i-1] == 'z' && s[i] == 'a'))
dp[i] += dp[i-1]; // 相当于dp[i] = dp[i-1]+1
// 哈希表用于找:以该字符为结尾的,最长的数组中,在base 的数组个数
int hash[26] = {0};
for(int i = 0; i < n; ++i)
hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);
// 返回值
int ret = 0;
for(int num : hash)
ret += num;
return ret;
}
};