赞
踩
题目链接:https://leetcode.cn/problems/longest-common-subsequence/
给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
"ace"
是 "abcde"
的子序列,但 "aec"
不是 "abcde"
的子序列。两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
提示:
1 <= text1.length, text2.length <= 1000
text1
和 text2
仅由小写英文字符组成。思路
状态转移方程:
text1[i] == text2[j]
),说明我们找到了一个新的公共字符,因此最长公共子序列的长度在之前的基础上加 1:
dp[i][j] = dp[i - 1][j - 1] + 1
text1[i] != text2[j]
),则我们需要考虑去掉 text1[i]
或者去掉 text2[j]
之后的最长公共子序列。这可以通过比较 text1
的前 i-1
个字符和 text2
的前 j
个字符的最长公共子序列长度,以及 text1
的前 i
个字符和 text2
的前 j-1
个字符的最长公共子序列长度,取最大值:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
初始化:为了方便处理边界情况,我们在文本 text1
和 text2
前各添加一个空字符,即 text1[0]
和 text2[0]
表示空串。这样,dp
表的大小为 (m+1) x (n+1)
,其中 m
和 n
分别是两个文本的长度。初始化时,将第一行和第一列的值都设置为 0。
填表顺序:最终的结果存储在 dp[m][n]
中,其中 m
和 n
是两个文本的长度。这个值表示 text1
和 text2
的最长公共子序列的长度。
代码
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m=text1.size(),n=text2.size();
text1=" "+text1,text2=" "+text2;
vector<vector<int>> dp(m+1,vector<int>(n+1));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
if(text1[i]==text2[j]) 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];
}
};
题目链接:https://leetcode.cn/problems/uncrossed-lines/
在两条独立的水平线上按给定的顺序写下 nums1
和 nums2
中的整数。
现在,可以绘制一些连接两个数字 nums1[i]
和 nums2[j]
的直线,这些直线需要同时满足满足:
nums1[i] == nums2[j]
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。
以这种方法绘制线条,并返回可以绘制的最大连线数。
示例 1:
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
示例 2:
输入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
输出:3
示例 3:
输入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
输出:2
提示:
1 <= nums1.length, nums2.length <= 500
1 <= nums1[i], nums2[j] <= 2000
思路
如果我们希望确保两条直线不相交,我们可以将问题转化为在两个整数数组中寻找最长的公共子序列。这是因为,如果两条直线不相交,它们在同一列上对应的两个元素在两个数组中的顺序关系应该保持一致。
这个问题可以用动态规划来解决,具体步骤如下:
m
和 n
。dp
,其中 dp[i][j]
表示数组1的前 i
个元素和数组2的前 j
个元素中的最长公共子序列的长度。nums1[i] == nums2[j]
,说明找到了一个公共元素,状态转移方程为 dp[i][j] = dp[i-1][j-1] + 1
。nums1[i] != nums2[j]
,则需要在两个数组的前面部分分别寻找最长公共子序列,状态转移方程为 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
。dp
数组的第一行和第一列为0,因为空串与任何数组的最长公共子序列长度都为0。dp
数组。dp[m][n]
中,表示两个数组的最长公共子序列的长度。代码
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size(),n=nums2.size();
vector<vector<int>> dp(m+1,vector<int>(n+1));
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]=max(dp[i-1][j],dp[i][j-1]);
return dp[m][n];
}
};
题目链接:https://leetcode.cn/problems/distinct-subsequences/
你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。
示例 1:
输入:s = "rabbbit", t = "rabbit"
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit
示例 2:
输入:s = "babgbag", t = "bag"
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。
babgbag
babgbag
babgbag
babgbag
babgbag
提示:
1 <= s.length, t.length <= 1000
s
和 t
由英文字母组成思路
状态表达:
[0, i]
区间以及第二个字符串 [0, j]
区间作为研究对象,结合题目要求来定义状态表达。dp[i][j]
,表示在字符串 s
的 [0, j]
区间内的所有子序列中,有多少个 t
字符串 [0, i]
区间内的子串。状态转移方程:
根据最后一个位置的元素,结合题目要求进行分类讨论:
当
t[i] == s[j]
时:
s[j]
作为结尾,相当于在状态 dp[i-1][j-1]
中的所有符合要求的子序列的后面,再加上一个字符 s[j]
,此时 dp[i][j] = dp[i-1][j-1]
。s[j]
作为结尾,相当于选择了状态 dp[i][j-1]
中所有符合要求的子序列,继承了上个状态里面的求得的子序列,此时 dp[i][j] = dp[i][j-1]
。当
t[i] != s[j]
时:
dp[i][j-1]
中选择所有符合要求的子序列,继承上个状态里面求得的子序列,此时 dp[i][j] = dp[i][j-1]
。综上,状态转移方程为:
dp[i][j] = dp[i][j-1]
。t[i] == s[j]
时,可以多选择一种情况:dp[i][j] += dp[i-1][j-1]
。初始化:
s
为空时,t
的子串中有一个空串和它一样,因此初始化第一行全部为 1
。填表顺序:
返回值:
dp[m][n]
的值。代码
class Solution { public: int numDistinct(string s, string t) { int m=t.size(),n=s.size(); vector<vector<double>> dp(m+1,vector<double>(n+1)); for(int j=0;j<=n;j++) 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]; if(t[i-1]==s[j-1]) dp[i][j]+=dp[i-1][j-1]; } return dp[m][n]; } };
题目链接:https://leetcode.cn/problems/wildcard-matching/
给你一个输入字符串 (s
) 和一个字符模式 (p
) ,请你实现一个支持 '?'
和 '*'
匹配规则的通配符匹配:
'?'
可以匹配任何单个字符。'*'
可以匹配任意字符序列(包括空字符序列)。判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。
示例 1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa", p = "*"
输出:true
解释:'*' 可以匹配任意字符串。
示例 3:
输入:s = "cb", p = "?a"
输出:false
解释:'?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
提示:
0 <= s.length, p.length <= 2000
s
仅由小写英文字母组成p
仅由小写英文字母、'?'
或 '*'
组成思路
在处理两个字符串之间的动态规划问题时,通常按照以下步骤进行:
状态表达:
[0, i]
区间以及第二个字符串 [0, j]
区间作为研究对象,结合题目的要求定义状态表达。dp[i][j]
,表示字符串 p
的 [0, j]
区间内的子串能否匹配字符串 s
的 [0, i]
区间内的子串。状态转移方程:
s[i] == p[j]
或 p[j] == '?'
时,两个字符串匹配上了当前的一个字符,只能从 dp[i-1][j-1]
中看当前字符前面的两个子串是否匹配,继承上个状态中的匹配结果,dp[i][j] = dp[i][j-1]
。p[j] == '*'
时,匹配策略有两种选择:
'*'
匹配空字符串,相当于它匹配了一个寂寞,直接继承状态 dp[i][j-1]
,dp[i][j] = dp[i][j-1]
。'*'
向前匹配 1 ~ n
个字符,直至匹配整个 s
串。相当于从 dp[k][j-1]
(0 <= k <= i) 中所有匹配情况中,选择性继承可以成功的情况,dp[i][j] = dp[k][j-1]
(0 <= k <= i)。p[j]
不是特殊字符且不与 s[i]
相等时,无法匹配。综上,状态转移方程为:
s[i] == p[j]
或 p[j] == '?'
时:dp[i][j] = dp[i-1][j-1]
。p[j] == '*'
时,状态转移方程优化为:dp[i][j] = dp[i-1][j] || dp[i][j-1]
。初始化:
dp
数组的值表示是否匹配,初始化整个数组为 false
。dp[0][0]
表示两个空串是否匹配,初始化为 true
。s
为空串,p
串与空串只有一种匹配可能,即 p
串表示为 "***"
,此时也相当于空串匹配上空串,将所有前导为 "*"
的 p
子串与空串的 dp
值设为 true
。p
为空串,不可能匹配上 s
串,跟随数组初始化即可。填表顺序:
返回值:
dp[m][n]
的值。代码
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)); dp[0][0]=true; 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][j-1]||dp[i-1][j]; else dp[i][j]=(p[j]=='?'||s[i]==p[j])&&dp[i-1][j-1]; } return dp[m][n]; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。