392.判断子序列、115.不同的子序列
392.判断子序列
一般解法
对于该题,我们的一般解法,就是遍历一次s数组,如果可以在t数组中顺序找到这些元素,那么我们可以判断s是t的自序列。因此,我们可以用两层循环,并且用k来记录我们遍历到t数组的位置,防止重复遍历。
因此,我们可以写出以下代码。
代码
class Solution {
public:
bool isSubsequence(string s, string t) {
int k = 0;
int u = 0;
if(s.size()>t.size()) return false;
for(int ii = 0;ii<s.size();ii++)
{
for(int jj = k;jj<t.size();jj++)
{
if(s[ii] == t[jj])
{
k = jj+1;
u++;
break;
}
}
}
if(u==s.size())return true;
return false;
}
};
动态规划
我们也可以用动态规划的思路去解决该问题,如同之前处理最长字符串的思路,我们也可以用dp数组来记录已经记录的公共子字符串长度。如果dp数组最后行最后列的元素记录为s数组的大小,说明s是t的子字符串。
动态规划四部曲。
1.dp数组的含义。
dp[ii][jj]表示数组s的下标为0-ii-1与数组t的下标为0-jj-1所能记载的最长公共自序列。这是和处理最长公共子序列的思路一致。
2.dp数组的递推公式。
if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+1;
else dp[ii][jj] = max(dp[ii-1][jj],dp[ii][jj-1]);
3.dp数组的初始化。我们令所有第一行和第一列的元素全为0,表示,下标0到下标-1的元素最长公共子序列长度为0,便于进行元素的递推。
4.先遍历哪一个数组都可以,这个没有必要在意。
代码
class Solution {
public:
bool isSubsequence(string s, string t) {
vector<vector<int>>dp(s.size()+1,vector<int>(t.size()+1,0));
for(int ii =1;ii<s.size()+1;ii++)
{
for(int jj =1;jj<t.size()+1;jj++)
{
if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+1;
else dp[ii][jj] = max(dp[ii-1][jj],dp[ii][jj-1]);
}
}
if(dp[s.size()][t.size()]==s.size()) return true;
else return false;
}
};
115.不同的子序列
这道题我们利用动态规划进行解决。
与其他子序列不同的是,这道题要求的是不同子序列的数量,不单单是判断是否是子序列以及子序列长度,因此用以往的思路进行解决已经无法满足于此问题。
首先,对于字符串中任意两个元素,他们都有相同与不相同两种情况,因此,我们需要进行不同的判断。
我们用dp[ii][jj]表示以元素s[ii-1]t[jj-1]为结尾的子序列种类。
如果遍历的两个字符串中的元素s[ii-1]与t[jj-1]相同,那么我们对dp[ii][jj]进行更新,我们需要分两种情况,使用s[ii-1]与不使用s[ii-1]。
如果使用s[ii-1],那么截止t[jj-1]其子序列数就应该是dp[ii-1][jj-1],因为s[ii-1]与t[jj-1]相互匹配的话,那么dp[ii-1][jj-1]就是两个序列的前一个元素的子序列数。
如果不使用s[ii-1],那么截止t[jj-1]其子序列数就应该是dp[ii-1][jj]。这是dp数组的定义。
因此如果两个字符串中的元素s[ii-1]与t[jj-1]相同,那么我们应该更新dp[ii][jj]为dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];
如果不相同,那么,我们继承不使用s[ii-1]的情况即可。dp[ii][jj] = dp[ii-1][jj];
初始化,对于数组的初始化,我们需要对第一列和第一行进行初始化。
根据dp数组的定义,如果t为空,空字符串应该是任何字符串的子集,所以令
dp[ii][0]=0
但是如果s为空,任何字符串(非空)不是空字符串的子集,因此令
dp[0][jj] = 0
动态规划四部曲。
1.dp数组的含义。dp[ii][jj]表示以元素s[ii-1]t[jj-1]为结尾的子序列种类。
2.dp数组的推导公式。
if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];
else dp[ii][jj] = dp[ii-1][jj];
3.dp数组的初始化。
dp[ii][0] = 1;//其余为0
4.遍历顺序,我们可以先访问s数组后访问t数组。
代码
class Solution {
public:
int numDistinct(string s, string t) {
if(s.size()<t.size())
return 0;
vector<vector<long long unsigned int>>dp(s.size()+1,vector<long long unsigned int>(t.size()+1,0));
for(int ii = 0;ii<s.size()+1;ii++)
dp[ii][0] = 1;
for(int ii = 1;ii<s.size()+1;ii++)
{
for(int jj =1;jj<t.size()+1;jj++)
{
if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];
else dp[ii][jj] = dp[ii-1][jj];
}
}
return dp[s.size()][t.size()];
}
};