文章目录
前言
一、1143. 最长公共子序列
看完解析之后可以尝试分析一下这道题目
1035. 不相交的线
1.状态表示
解决此类题目我们的经验就是: 以第一个字符串的区间【0,i】以及第二个字符串的区间【0,j】为研究对象,根据题目要求确立状态表示
根据这个题目,状态标识可以设置为
第一个字符串的区间【0,i】以及第二个字符串的区间【0,j】为结尾的所有子序列中最长公共子序列的长度
2.状态转移方程
我们依然是根据经验:根据最后一个位置,分情况讨论
对于dp【i】【j】,我们可以对s【i】和s【j】的情况进行讨论。
🌟s【i】和s【j】相等,那就说明最长公共子序列一定是以i和j为结尾的。
我们只需要找到【0,i-1】和【0,j-1】区间内最长子序列长度再加上1就是我们要找的dp值。
🌟s【i】和s【j】不相同,那就说明在这个区间内最仓公共子序列不是以i和j同时结尾的,我们只需要在这里面找最大值就可以,dp【i-1】【j】,dp【i】【j-1】,dp【i-1】【j-1】。
这里还有一个问题,【i-1】【j-1】区间包括在另外两个区间里面了,我们要不要将他舍去呢???
根据本道题木,我们要求的是最长的长度,舍去对结果没有影响们可以舍去。
3.初始化
关于此类字符串dp问题,我们要考虑空串的情况。
考虑空串之后,可以很方便我们进行初始化,多加上一行和一列就可以。
同时要保证后序填表正确和下标的映射关系。
🌟对于初始化的值,我们把这一行和一列初始化为0就可以。
🌟对于下标的映射关系,字符串可以采用在前面多加一个字符来解决。
4.填表顺序
从左到右,从上到下
5.返回值是什么
返回dp[ m ][ n]
6.代码编写
class Solution {
public:
int longestCommonSubsequence(string s1, string s2)
{
int m=s1.size();
int n=s2.size();
//1.建表+初始化
vector<vector<int>>dp(m+1,vector<int>(n+1,0));
//2.处理字符串,方便填表
s1=" "+s1;
s2=" "+s2;
//3.填表
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
//4.返回值
return dp[m][n];
}
};
二、44. 通配符匹配
1.状态表示
我们根据经验+题目要求,确定出状态标示:
p【0,j】区间内的字串能否匹配s【0,i】区间的字串。
2.状态转移方程
根据最后一个位置的情况划分问题
我们可以知道p【j】有三种情况,分析一下。
🌟p【j】是普通小写字母。如果能够匹配,首先要保证最后一个位置的字母相同即可。随后在p【0,j-1】区间内判断s【0,i-1】区间内能否匹配就可以,这正好就是我们的状态表示,dp【i-1】【j-1】。
🌟p【j】是‘ ?’,?可以匹配任意一个字母,那么最后一个位置肯定可以匹配,我么只需要判断剩余区间就可以,也就是判断dp【i-1】【j-1】.
🌟p【j】是‘ * ’,它可以匹配多个字母,但是在题目中我们并不能确定这个星号需要匹配多少个字母,我们逐个分析一下。
🎈假设星号一个也不匹配,就是匹配空串,我们就只需要判断dp【i】【j-1】
🎈假设星号匹配1个,我们就只需要判断dp【i-1】【j-1】
🎈假设星号匹配2个,我们就只需要判断dp【i-2】【j-1】
🎈假设星号匹配3个,我们就只需要判断dp【i-3】【j-1】
🎈… … … … …
🎈假设星号匹配i个,我们就只需要判断dp【0】【j-1】
这些情况我们需要挨个判断,只要有一个结果为true,dp【i】【j】就为true.
我们在这里可以进行一些小优化:
当 dp[j] == ‘*’ 时,状态转移方程为:
dp[i][j] = dp[i][j - 1] || dp[i - 1][j - 1] || dp[i - 2][j - 1] …
我们发现 i 是有规律的减小的,因此我们去看看 dp[i - 1][j]
dp[i - 1][j] = dp[i - 1][j - 1] || dp[i - 2][j - 1] || dp[i - 3][j - 1] …
我们惊奇的发现, dp[i][j] 的状态转移方程里面除了第⼀项以外,其余的都可以用 dp[i - 1][j] 替代。
因此,我们优化我们的状态转移方程为: dp[i][j] = dp[i - 1][j] || dp[i][j - 1] 。
3.初始化
我们在处理字符串问题时候,要考虑空串。
空串:多开一行和一列解决,同时保证辅助结点里面的值要保证后续填表是正确的,还要注意下标的映射关系。
根据分析,dp【0】【0】为true;
这里不要把其余位置都设置为false.
*注意:可以匹配空串,所以第一行需要特殊判断。
4.填表顺序
填表的顺序是「从上往下,从左往右」
5.返回值是什么
返回dp【m】【n】的值即可
6.代码编写
class Solution {
public:
bool isMatch(string s, string p)
{
int m=s.size();
int n=p.size();
//建表
vector<vector<bool>>dp(m+1,vector<bool>(n+1,false));
//初始化
dp[0][0]=true;
s=" "+s;
p=" "+p;
//*很特殊,可以匹配空串
for(int i=1;i<=n;i++)
{
if(p[i]=='*') dp[0][i]=true;
else break;
}
//填表
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(p[j]=='?') dp[i][j]=dp[i-1][j-1];
else if(p[j]=='*') dp[i][j]=dp[i][j-1]||dp[i-1][j];
else
{
if(s[i]==p[j]&&dp[i-1][j-1]==true) dp[i][j]=true;
}
}
}
//返回值
return dp[m][n];
}
};
三、712. 两个字符串的最小ASCII删除和
712. 两个字符串的最小ASCII删除和
本道题木,我们只提供部分讲解,这就是这道题目的精髓
正难则反:我们要求删除的字符最小,那么反过来我们也可以通过找到两个字符串中相同的字符的ASCII码值最大就可以,最后再用总大小减去这个就是我们所要求的结果。
class Solution {
public:
int minimumDeleteSum(string s1, string s2)
{
int m=s1.size();
int n=s2.size();
int sum=0;
for(int i=0;i<m;i++)
{
sum+=s1[i];
}
for(int j=0;j<n;j++)
{
sum+=s2[j];
}
//建表
vector<vector<int>>dp(m+1,vector<int>(n+1,0));
//初始化
s1=" "+s1;
s2=" "+s2;
//填表
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+s1[i];
dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i][j-1]));
}
}
return sum-dp[m][n]*2;
}
};
总结
以上就是今天要讲的内容,本文仅仅详细介绍了 。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘