718. Maximum Length of Repeated Subarray
给定两个序列,求同时出现在两个序列中的最长子列的长度。
思路:因为相同子列可以出现在两个序列的任意位置,所以比较直接的想法是用两个嵌套的循环实现对两个序列中元素的遍历,并且结合动态规划。
顺便记录一下二维数组的初始化方式:vector<vector<int>> dp(A.size()+1,vector<int>(B.size()+1,0));
class Solution {
public:
int findLength(vector<int>& A, vector<int>& B) {
//简单粗暴的动态规划,dp[a][b]表示从A序列的a位置到末尾和B序列的b位置到末尾的最长相同子列
vector<vector<int>> dp(A.size()+1,vector<int>(B.size()+1,0));
int ans = 0;
for(int a = A.size()-1;a>=0;--a)
for(int b=B.size()-1;b>=0;--b)
{
//只有当满足if的条件才回修改dp数组的值,所以有一部分dp的值为0
//这样才能保证所找到的共同子列满足子列的性质
if(A[a]==B[b])
dp[a][b]=dp[a+1][b+1]+1;
ans = max(ans,dp[a][b]);
}
return ans;
}
};
894. All Possible Full Binary Trees
给定节点总数量,求所有可能的完全二叉树(每一个节点要么没有子节点要么有两个子节点)
一开始我自己想得太简单,以为用回溯就可以了,结果做不出来,后来看了Discuss里巧妙的递归解法,感觉掌握了这种解法会使自己对递归有进一步的认识。
思路:首先我们可以发现,题目定义的完全二叉树是不可能含有偶数个节点的,所以我们只需要考虑节点总数是奇数的情况。所以对于一棵完全二叉树,如果总节点数为N,那么其左子树可以有1,3,...个节点(不超过N),对应的其右子树可以有N-1-1,N-3-1,...个节点(不超过N)。所以我们自底向上地先构造根节点的左子树和右子树,再构造根节点,以此来形成不同的完全二叉树。
vector<TreeNode*> allPossibleFBT(int N)
{
vector<TreeNode*> ans;
unordered_map<int,vector<TreeNode*>> m; //记忆化搜索,剪枝
if(N%2==0||N<1)
return ans;
if(N==1)
{
TreeNode* root = new TreeNode(0);
ans.push_back(root);
return ans;
}
vector<TreeNode*> lefttree,righttree;
for(int left=1;left<N;left+=2)
{
if(m.count(left))
lefttree = m[left];
else
lefttree = allPossibleFBT(left),m[left] = lefttree;
int right = N-left-1;
if(m.count(right))
righttree = m[right];
else
righttree = allPossibleFBT(right),m[right] = righttree;
//lefttree、righttree里存放的是不同完全二叉树的根节点,所以以lefttree的不同树为左子树,以righttree的不同树为右子树可以创建新的完全二叉树。
for(int l=0;l<lefttree.size();++l)
for(int r=0;r<righttree.size();++r)
{
TreeNode* root = new TreeNode(0);
root->left = lefttree[l];
root->right = righttree[r];
ans.push_back(root);
}
}
return ans;
}
730. Count Different Palindromic Subsequences
给定一个字符串,求有多少个回文子串。这里的子串不一定要连续。
这题我也不会,子串可以不是连续的,觉得比较复杂,没有什么思路,Discuss里的讨论也不多,但大家的思路都挺相似的。
思路:动态规划,按子串的长度分类讨论。用动态规划数组dp[i][j](i < j)表示字符串从下标i到下标j共有多少个回文子串。我们从子串长度为1开始,扩展到子串长度为N(N=S的长度),这样其实像是把整个问题分为不同相关联的小部分去组成,由最简单的情况层层往上,联系已经计算好的个数计算新长度下回文子串的个数。
若S[i] != S[j],则dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1];这比较好理解,只要理解我们的dp数组是表示什么就可以明白了。
若S[i]==S[j],则需分三种情况考虑,这里借用此博客中的图(感谢博主!),我也是学习里面的思路才渐渐懂的。
图片的上两部分解释了为什么S[i]==S[j]时要分情况讨论,最下面那一部分说明了dp数组的计算。
通过实例比较好理解,所以我直接放上图片。
int countPalindromicSubsequences(string S) {
if(S.empty())
return 0;
int n = S.size(),module = 1000000007;
// 用long类型 防止中间计算时溢出
vector<vector<long>> dp(n,vector<long>(n,0));
//每一个单独的字符都是一个回文串
for(int i=0;i<n;++i)
dp[i][i] = 1;
//按照上述思路计算
for(int len=1;len<n;++len)
for(int i=0;i<n-len;++i)
{
int j = i+len;
if(S[i]==S[j])
{
dp[i][j] = dp[i+1][j-1]*2;
int left=i+1,right=j-1;
while(left<=right&&S[left]!=S[i])
left++;
while(left<=right&&S[right]!=S[i])
right--;
if(left == right)
dp[i][j]++;
else if(left<right)
dp[i][j]-=dp[left+1][right-1];
else
dp[i][j]+=2;
}
else
dp[i][j] = dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];
dp[i][j] = (dp[i][j]+module) % module;
}
return dp[0][n-1];
}
前段时间没有连续写下去,再上Leetcode时已经不知道我整理到哪了... 这一系列可能到此为止了。开学了,该继续刷题了。