在这里说明,我写的斐波那契数列模型是依托答辩,不建议看。这段时间比较懒,所以子数组,简单的多态,这几个会在最后写。
两个数组也是让我头脑发烫的难题。
1.最长公共子序列
class Solution {
public:
int longestCommonSubsequence(string s, string t) {
int m=s.size(),n=t.size();
//以s[i]为结尾和以t[j]结尾,确定最长公共子序列(长度)
//情况分类:dp[i][j]=s[i-1]==t[j-1]?dp[i-1][j-1]+1:max(dp[i-1][j],dp[i][j-1]);
//当s[i-1]==t[j-1]时取dp[i-1][j-1]+1(+1表示增加1的长度),否则保留当前最大子序列长度
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(s[i-1]==t[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];
}
};
2. 最长重复子数组
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size(),n=nums2.size();
//该题与最长公共子序列基本相同
//唯一区别在于nums1[i-1]!=nums2[j-1]时,dp[i][j]不需要保留当前最长子数组
vector<vector<int>> dp(m+1,vector<int>(n+1));
//小技巧:我们在nums头部插入一个数
//我们就可以写if(nums1[i]==nums[j])且让i,j从1开始遍历
int res=dp[0][0];
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
if(nums1[i-1]==nums2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=0;
res=max(dp[i][j],res);
}
}
return res;
}
};
如果心细的你还会发现(最长回文子序列)这题与1,2题相似。
3.不相交的线
class Solution {
public:
int maxUncrossedLines(vector<int>& s, vector<int>& t) {
int m=s.size(),n=t.size();
//不相交==子序列,最大连线数==最长公共子序列
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(s[i-1]==t[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];
}
};
这些开胃小菜也就吃完了,我们开始真正的修行把。
4.不同的子序列
class Solution {
public:
int numDistinct(string s, string t) {
const int MOD=1e9+7;
int m=t.size(),n=s.size();
//在s[j]中有多少个t[i]子序列(状态表示)
//状态转移方程:s[j-1]==t[i-1]->dp[i-1][j-1]
//继承dp[i][j-1]->如果出现s=dagg,t=dag,dp[i-1][j-1]完全不够。(这里建议看代码随想录)
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
for(int j=0;j<=n;++j)dp[0][j]=1;//当t.empty()=true,dp[0][j]=1
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
dp[i][j]=dp[i][j-1];//不选s[j]
if(s[j-1]==t[i-1])//符合条件就选
dp[i][j]=(dp[i][j]+dp[i-1][j-1])%MOD;
}
}
return dp[m][n];
}
};
5.通配符匹配
class Solution {
public:
bool isMatch(string s, string p) {
int m=s.size(),n=p.size();
s=' '+s,p=' '+p;//优化:不需要对p and s进行-1操作
vector<vector<bool>> dp(m+1,vector<bool>(n+1));
dp[0][0]=true;//当s.empty()==p.empty()==true
for(int j=1;j<=n;++j)//*可以匹配空字符
if(p[j]=='*')dp[0][j]=true;
else break;
//这里可以分为*与非*两种情况
//1.非*:当p[j]为字母时:p[j]==s[i]&&dp[i-1][j-1]才能匹配,当p[j]=='?'只需要判断dp[i-1][j-1]
//2.*:分为*代表空,代表1个字符,代表2个字符,3个字符.....n个字符
//空时:判断s:0~i是否与p:0~j-1相等->dp[i][j-1];
//1个:dp[i-1][j-1] 2个:dp[i-2][j-1] 3个:dp[i-3][j-1]....n个:dp[i-n][j-1]
//如果我们再写一个for循环来表示*情况,时间复杂度就是O(n^3)可能超时,所以我们必须优化为O(1)
//dp[i][j]=dp[i][j-1]||dp[i-1][j-1]||dp[i-2][j-1]||dp[i-3][j-1]||....dp[i-n][j-1]
//可以推导出dp[i-1][j]=dp[i-1][j-1]||dp[i-2][j-1]||dp[i-3][j-1]||....dp[i-n][j-1]
//由此可知dp[i][j]=dp[i][j-1]||dp[i-1][j]
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
if(p[j]=='*')
dp[i][j]=dp[i-1][j]||dp[i][j-1];
else
dp[i][j]=dp[i-1][j-1]&&(s[i]==p[j]||p[j]=='?');
}
}
return dp[m][n];
}
};
这题非常有意思对吧。
6.正则表达式匹配
class Solution {
public:
bool isMatch(string s, string p) {
int m=s.size(),n=p.size();
s=' '+s,p=' '+p;
//这题的唯一区别在于*与前一个字符绑定了,也就是说*想表示空,会连同前一个字符一起删除
vector<vector<bool>> dp(m+1,vector<bool>(n+1,false));
dp[0][0]=true;
for(int j=2;j<=n;j+=2)//如果连续的间隔的存在*,就可以表示空与s为空时匹配
if(p[j]=='*')dp[0][j]=true;
else break;
//p[j]=='*'&&p[j-1]为字母时分为两种情况:空 or n个
//空:s:0~i与p:0~j-2相同时true,也就是dp[i][j-2]
//n个:当s[i]==p[j-1]&&dp[i-1][j-2]||s[i-1]==p[j-1]&&dp[i-2][j-2]...s[i-n-1]==p[j-1]&&dp[i-n][j-2]
//这不就是(通配符匹配)中的dp[i-1][j]再ands[i]==p[j-1]即可
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
if(p[j]=='*'){
if(p[j-1]=='.')//这里与(通配符匹配)道理相同,只不过这次是 ?+*
dp[i][j]=dp[i-1][j]||dp[i][j-2];
else
dp[i][j]=dp[i][j-2]||(dp[i-1][j]&&s[i]==p[j-1]);
}
else
dp[i][j]=dp[i-1][j-1]&&(s[i]==p[j]||p[j]=='.');
}
}
return dp[m][n];
}
};
如果你还意犹未尽可以娱乐一下这两题:交错字符串 and 两个字符串的最小ASCII删除和.
结束....