LIS & LCS
《动态规划初步·各种子序列问题》,作者是Flower_pks.动态规划初步·各种子序列问题
最长上升子序列(LIS):
- 元素不一定相邻
- O(n^2)
- dp[i]:到i元素为止的最长上升子序列长度,初始值为1;暴力枚举i之前的所有子串
data[N],booK[N];//记录数组
void out(int x){//输出最长子序列
if(!x) return;
out(book[x]);
cout << data[x] << ' ';//回溯 输出答案
}
for(int i = 1; i <= n; i++){
dp[i] = 1;
book[i] = 0;
for(int j = 1; j < i; j++){//枚举i之前的所有j
//i元素之前小于他的j元素满足:dp[j] + 1 大于 当前最长上升子序列长
//dp[1] - dp[n] 维护最大值
if(data[j] < data[i] && dp[i] < dp[j] + 1) dp[i] = dp[j] + 1,book[i] = j;
}
}
int ans = dp[1],pos = 1;
for(int i = 1; i <= n; i++){
if(ans < dp[i]) ans = dp[i],pos = i;//记录最大值的索引
}
cout << ans << endl;
out(pos);
- O(nogn):
//dp[i]表示长度为i的上升子序列的最小末尾元素
//当a[i] > dp[len] 时上升子序列长度 + 1,dp[++len] = a[i]
//else 二分搜索修改其它长度的上升子序列的最小末尾
int len = 1;
dp[1] = a[1];
for(int i = 2; i <= n; i++){
if(a[i] > dp[len]) dp[++len] = a[i];
else{
//二分更新最小末尾
int l = 1, r = len,mid;
while(l < r){
mid = (l + r) >> 1;
if(dp[mid] >= a[i]) r = mid;
else l = mid + 1;
}
dp[l] = min(a[i], dp[l]);//更新对应长度上升子序列的最小末尾
}
}
cout << len;
最长公共子序列(LCS)
//dp[i][j]:a串前i,b串前j个元素的最长公共子序列
//if(a[i] == b[j])dp[i][j] = max(dp[i][j],dp[i - 1][j - 1] + 1);
//else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
cin >> n >> m;//a b串长度
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= m; i++) cin >> b[i];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
if(a[i] == b[j]) dp[i][j] = max(dp[i][j],dp[i - 1][j - 1] + 1);
}
}
cout << dp[n][m];