Topic 1:最长回文子串(动态规划、manacher、中心扩展算法)
这种题一般解决就是三个解法——动态规划、马拉车(manacher)算法、中心扩展算法,但是马拉车算法不通用,只能解决这一道题,应用价值不高,可以以后再了解,动态规划的方法倒是通用,还能知道所有字符的回文情况,但空间复杂度比较高,这里还是用中心扩展法
class Solution {
public:
// 从中心向两边扩展,返回以left和right为中心的最长回文长度
int exp(const string& s, int left, int right)
{
while (left >= 0 && right < s.size() && s[left] == s[right]) // 不会越界,越界检查条件在前
{
left--; // 向左扩展
right++; // 向右扩展
}
return right - left - 1; // 返回回文长度 (right - left - 1) a b a
}
int getLongestPalindrome(string A) {
if (A.empty()) return 0;
int start = 0, end = 0; // 记录最长回文子串的起始和结束位置
for (int i = 0; i < A.size(); i++)
{
int len1 = exp(A, i, i); // 奇数长度回文,中心为i
int len2 = exp(A, i, i + 1);// 偶数长度回文,中心为i和i+1
int len = max(len1, len2);// 取两种情况的较大值
if (len > end - start) // 如果找到更长的回文,更新起始和结束位置
{
start = i - (len - 1) / 2; // 计算回文起始,整数除法,可以带进去算算,奇偶数都正确
end = i + len / 2; // 计算回文结束
}
}
return end - start + 1;
}
};
Topic 2:买卖股票的最好时机(一)(动态规划、贪心)
由于题目时间轴和一次交易的特性,可以作图
图中红色部分表示在对应两天中,所作交易产生的利润,绿色为如果在此间交易,会承担的亏损,如果不限交易次数,那么我们收集所有的红色交易部分,即可获取11天交易的最大利润。
但是现在限制我们只能交易1次,那么我们不需要关注所有获利交易,只需要按照时间轴,从左往右找获利交易中的最小值,固定最小值,然后让后续的价格与之相减,拿到最大价值,若发现新最小值,则更新最小值,并重复计算,不断更新最大价值,最后留下来的就是全局所能得到的最大价值,即可求出该区间中买卖的最好时机,这是贪心的思路。后续还有动态规划的方法,那个是通用解法,有时间要用DP再做一遍;
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param prices int整型vector
* @return int整型
*/
int maxProfit(vector<int>& prices)
{
if (prices.size() <= 1) return 0;
int min_price = INT_MAX;// 股价最低点
int max_profit = 0;// 最大价值
for (int price : prices)
{
if (price < min_price)
{
min_price = price;// 发现还有更低点,则更新
}
else if (price - min_price > max_profit) // 否则查看当前价值-最低点是否大于之前记录的最大价值
{
max_profit = price - min_price;// 如果大于就更新最大价值
}
}
return max_profit;
}
};
再简化一下
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() <= 1) return 0;
int min_price = INT_MAX;// 股价最低点
int max_profit = 0;// 最大价值
for (int price : prices)
{
min_price = min(min_price, price);// 发现还有更低点,则更新
max_profit = max(max_profit, price - min_price);// 如果大于就更新最大价值
}
return max_profit;
}
};
Topic 3:过河卒(动态规划)
经典的动态规划,dp[n,m]——走到坐标n,m所有的路径数
dp[n,m] = dp[n - 1, m] + dp[n,m - 1],递推公式,很简单就能想到
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m, x, y;
cin >> n >> m >> x >> y;
vector<vector<bool>> blocked(21, vector<bool>(21, false)); //记录马的封锁区 数组大小20 + 1覆盖所有可能的坐标
if (x >= 0 && x <= n && y >= 0 && y <= m) // 马的位置
{
blocked[x][y] = true;
}
// 马的控制点,也可以用公式法 if (abs(x1 - x) + abs(y1 - y) == 3 && x1 != x && y1 != y)
int dx[] = {1, 1, -1, -1, 2, 2, -2, -2};
int dy[] = {2, -2, 2, -2, 1, -1, 1, -1};
for (int i = 0; i < 8; ++i)
{
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx <= n && ny >= 0 && ny <= m)
{
blocked[nx][ny] = true;
}
}
vector<vector<long long>> dp(21, vector<long long>(21, 0));
dp[0][0] = 1; // 起点
for (int i = 0; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
if (blocked[i][j])
{
continue; // 跳过马的控制点
}
if (i == 0 && j == 0)
{
continue; // 起点已初始化
}
if (i > 0)
{
dp[i][j] += dp[i - 1][j];
}
if (j > 0)
{
dp[i][j] += dp[i][j - 1];
}
}
}
cout << dp[n][m] << endl;
return 0;
}