1 序列解题思路
序列的特点是不连续,序列相关的题目主要是子序列和字符串序列。这两类题目主要使用动态规划来解决。
对比子数组和子字符串的题目:LeetCode-子数组-子字符串(也就是连续的序列)_hclbeloved的博客-CSDN博客
2 子序列相关题目
2.1 最长递增子序列
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size(), maxLen = 0;
if (n == 0)
{
return 0;
}
//dp[i]: 以索引 i 处数字结尾的最长上升子序列的长度
vector<int> dp(n, 1);
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < i; ++j)
{
if (nums[i] > nums[j])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
maxLen = std::max(maxLen, dp[i]);
}
return maxLen;
}
};
2.2 最长连续序列
注意这里的连续指的是数字大小的连续性,并不是数组中数据前后的连续性,所以将这个题目放到了这里。
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
//方法一:最小堆,不满足O(n),实际上是o(nlogn)
// priority_queue<int, vector<int>, greater<int>> minHeap;
// unordered_set<int> s;
// int longest = 0;
// for(auto& i : nums)
// {
// if (!s.count(i))
// {
// s.insert(i);
// minHeap.push(i);
// }
// }
// while(!minHeap.empty())
// {
// int start = minHeap.top(), wanted = start+1, count = 1;
// minHeap.pop();
// while(minHeap.top() == wanted)
// {
// ++wanted;
// ++count;
// minHeap.pop();
// }
// longest = std::max(longest, count);
// }
// return longest;
// 方法二: 满足O(n)
unordered_set<int> s;
for(auto& i : nums)
s.insert(i);
int longest = 0;
for (auto& i : s)
{
if (!s.count(i-1))
{
int start = i, wanted = start+1, count = 1;
while (s.count(wanted))
{
++wanted;
++count;
}
longest = std::max(longest, count);
}
}
return longest;
}
};
2.3
2.4
2.5
3 字符串序列相关题目
3.1 最长公共子序列
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//从底到上的动态规划
int n = text1.length(), m = text2.length();
if (0 == n*m)
return 0;
//dp[i][j]: text1[0...i-1]与text2[0...j-1]的最长公共子序列的长度
//dp[0][j] 表示text1[0...-1]与text2[0...j-1]的最长公共子序列的长度,可理解为text1此时字符串长度为0;
//dp[i][0] 有类似的含义
//或者这样理解,dp[i][j]表示text1前i个元素与text2的前j个元素的最长公共子序列的长度
vector<vector<int>> dp(n+1, vector<int>(m+1,0));
//base case
for (int i=0;i<=n;++i)
dp[i][0] = 0;
for (int i=0;i<=m;++i)
dp[0][i] = 0;
//状态转移方程
for(int i=1;i<=n;++i)
{
for (int j=1;j<=m;++j)
{
if (text1[i-1] == text2[j-1])
{
dp[i][j] = 1 + dp[i-1][j-1];
}
else
{
dp[i][j] = std::max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[n][m];
}
};
3.2 子序列的数目
class Solution {
public:
//dp[i][j]表示 t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数
vector<vector<int>> dp;
int numDistinct(string s, string t) {
//方法三:从上到下(将大问题转化为小问题的递归求解)的动态规划
int n = s.length(), m = t.length();
if (n < m)
return 0;
else if (n == m)
return s==t ? 1 : 0;
if (m == 0)
return 1;
if (dp.empty())
dp.assign(n+1, vector<int>(m+1, -1));
if (-1 != dp[n][m])
return dp[n][m];
//从后向前在“源串”中查找第一个等于“模式串”最后一个字符的索引
for (int j=n-1;j>=0;--j)
{
if (s[j] != t[m-1])
continue;
// dp[i][j]表示 t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数
//状态转移方程:当 s[n-1] == t[m-1] 时,dp[n][m] = dp[n-1][m-1] + dp[n-1][m]
dp[n][m] = numDistinct(s.substr(0,j), t.substr(0,m-1)) + numDistinct(s.substr(0,j), t);
break;//很有必要,for循环的目的是找到s[j] == t[m-1]
}
// dp[n][m] = -1,说明上面的for循环没有执行到 dp[n][m] = numDistinct(s.substr(0,j), t.substr(0,m-1)) + numDistinct(s.substr(0,j), t);
// 就说明此时 “t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数为0”
dp[n][m] = (dp[n][m] == -1 ? 0 : dp[n][m]);
return dp[n][m];
}
};
3.3
3.4
3.5