最长上升子序列(二分)
问题分析:求解给定数组a的最长上升子序列的长度。
子序列:一个给定序列的子序列是在该序列中删去若干元素后得到的序列
时间复杂度:O(nlogn)
核心代码:
for(int i=1;i<=n;i++)
{
if(dp[cnt]<a[i]) dp[++cnt]=a[i];
else *lower_bound(dp+1,dp+cnt+1,a[i])=a[i];
}
过程详解:
for
循环遍历数组a,从索引1到索引n。if (dp[cnt] < a[i])
判断当前元素a[i]是否大于最长上升子序列的最后一个元素。如果是,将a[i]添加到最长上升子序列的末尾,并更新计数器cnt的值。else
如果a[i]不大于最长上升子序列的最后一个元素,则使用 lower_bound 函数找到dp数组中第一个大于等于a[i]的元素的位置,然后将a[i]赋值给该位置,实现替换操作。
样例详解:
假设a数组为【2 1 3 2 3】,每一步的dp数组如下:
- 【2】
- 【1】
- 【1 3】
- 【1 2】
- 【1 2 3】
dp[i]
的含义是:长度为i的上升序列中结尾最小的值。
因为这样更有利于我们在后面接上其他值,比如【1,2】和【2,3】,显然【1,2】后面可以接3,而【2,3】却不行。
最长下降子序列
原理同上
代码如下:
for(int i=1;i<=n;i++)
{
if(dp[cnt]>a[i]) dp[++cnt]=a[i];
else *lower_bound(dp+1,dp+cnt+1,a[i],greater<int>())=a[i];
}
最长公共子序列(动态规划)
问题分析:求解给定数组a和b的最长公共子序列的长度。
时间复杂度:O(NM),N和M分别代表a和b的长度。
核心代码:
f[i][j]=max(f[i-1][j], f[i][j-1]);
if(a[i]==b[j])
f[i][j]=max(f[i][j], f[i-1][j-1]+1);
f[i][j]
的含义是:字符串a的前i个字符和字符串b的前j个字符的最长公共子序列的长度。
核心代码的第一行表示状态转移方程:
f[i][j] = max(f[i-1][j], f[i][j-1])
它表示在考虑a[i]和b[j]字符时,最长公共子序列的长度要么等于字符串a的前i-1个字符和字符串b的前j个字符的最长公共子序列的长度,要么等于字符串a的前i个字符和字符串b的前j-1个字符的最长公共子序列的长度。即,我们要么选择不考虑a[i]字符,要么选择不考虑b[j]字符。
接下来的if语句用于处理当a[i]等于b[j]时的情况:
if(a[i] == b[j])
f[i][j] = max(f[i][j], f[i-1][j-1] + 1)
它表示如果a[i]等于b[j],那么最长公共子序列的长度可以在不考虑a[i]和b[j]的情况下加1。因此,我们在上述两个选择中选择更大的长度。