一、简单DP
礼物最大价值(矩阵贪心类题目)剑指 Offer 47
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
for(int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (i == 0 && j ==0) {
continue;
}
if (i == 0) {
grid[i][j] += grid[i][j - 1];
}
else if (j == 0) {
grid[i][j] += grid[i - 1][j];
}
else {
grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
}
}
}
return grid[m - 1][n - 1];
}
};
时间复杂度 O(MN):M, N分别为矩阵行高、列宽;动态规划需遍历整个grid矩阵,使用 O(MN)时间。
空间复杂度 O(1):原地修改使用常数大小的额外空间。
爬楼梯 剑指 Offer 10- II.
class Solution {
public:
int numWays(int n) {
if (n == 0 || n == 1)
{
return 1;
}
if (n == 2)
{
return 2;
}
int a = 1, b = 2;
int res = 0, MOD = 1000000007;
for (int i = 3; i <= n; i++)
{
res = (a + b) % MOD;
a = b;
b = res;
}
return res ;
}
};
时间复杂度O(n),空间复杂度O(1)
二、字符串+DP
编辑距离 leetcode 72
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length();
if (m * n == 0) {
return m + n;
}
int dp[m + 1][n + 1];
for (int i = 0; i <= m; i++)
{
dp[i][0] = i; // 当word2为空时, word1需要删掉i个字符
}
for (int j = 0; j <= n; j++)
{
dp[0][j] = j;
}
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
int flag = (word1[i - 1] == word2[j - 1] ? 0 : 1);
dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + flag));
}
}
return dp[m][n];
}
};
最长公共子序列 leetcode 1143
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.length(), n = text2.length();
if (m * n == 0) {
return 0;
}
int dp[m + 1][n + 1];
memset(dp, 0, sizeof(dp));
// vector<vector<int> > dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
if(text1[i - 1] == text2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
};
最长回文字串 leetcode 5
中心拓展算法
class Solution {
public:
pair<int, int> expandAroundCenter(const string& s, int left, int right)
{
while (left >= 0 && right < s.size() && s[left] == s[right])
{
--left;
++right;
}
return {left + 1, right - 1};
}
string longestPalindrome(string s) {
int slen = s.length();
if (slen == 1)
{
return s;
}
int start = 0, end = 0;
for (int i = 0; i < s.size(); ++i)
{
auto [left1, right1] = expandAroundCenter(s, i, i);
auto [left2, right2] = expandAroundCenter(s, i, i + 1);
if (right1 - left1 > end - start) // 相当于
{
start = left1;
end = right1;
}
if (right2 - left2 > end - start)
{
start = left2;
end = right2;
}
}
return s.substr(start, end - start + 1);
}
};
能看懂的写法
class Solution {
public:
string longestPalindrome(string s) {
int slen = s.length();
if (slen == 1)
{
return s;
}
int max_left = 0, max_right = -1;
for (int i = 0; i < slen; ++i)
{
int left = i, right = i;
while (left >= 0 && s[left] == s[i]) --left;
while (right < slen && s[right] == s[i]) ++right;
while (left >= 0 && right < slen && s[left] == s[right])
{
--left; ++right;
}
if (max_right - max_left < right - left)
{
max_left = left;
max_right = right;
}
}
return s.substr(max_left + 1, max_right - max_left - 1);
}
};
最长不重复子串 leetcode 3
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
occ = set()
slen = len(s)
# 右指针,初始值为-1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans = -1, 0
for i in range(slen):
if i != 0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i - 1])
while rk + 1 < slen and s[rk + 1] not in occ:
# 不断的移动右指针
occ.add(s[rk + 1])
rk += 1
# 第 i 到 rk个字符是一个极长的无重复字符串子串
ans = max(ans, rk - i + 1)
return ans