萌新关于LCS(最长相同子序列)题目汇总
LCS也是经典DP问题啦~O(nm)的时间复杂度,滚动数组优化空间复杂度
A.Common Subsequence POJ - 1458
LCS的模板题~
tips:读字符串的时候是从下标为0开始的,不是1哦
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
char str1[1005],str2[1005];
int dp[1005][1005];
int main(){
while(~scanf("%s %s",str1,str2)){
memset(dp,0,sizeof(dp));
int len1 = strlen(str1), len2 = strlen(str2);
for(int i = 1; i <= len1; ++i){
for(int j = 1; j <= len2; ++j){
if(str1[i - 1] == str2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
printf("%d\n",dp[len1][len2]);
}
return 0;
}
B.Palindrome POJ - 1159 / 洛谷P1435 回文字串
tips:将原字符串逆序得到另一个字符串,求它们的最长公共子序列,这样就能求得它的可以构成回文的最长字符数.(正序与倒序“公共”的部分就是回文的部分)
注意POJ 1159开5005*5005的int会爆内存~要开成short!
另外逆序字符串可以不用新开一个数组,也可以直接在原数组上从后往前循环。(注意algorithm里的reverse没有返回值哦)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int len;
short dp[5005][5005];
string s1,s2;
int main(){
scanf("%d",&len);
cin >> s1;
s2 = s1;
reverse(s1.begin(), s1.end());
for(int i = 1; i <= len; ++i){
for(int j = 1; j <= len; ++j)
if(s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
printf("%d",len - dp[len][len]);
return 0;
}
其实空间可以再优化~就想01背包一样,dp数组只会用到i - 1行的数据,0-(i-2)行的数据是用不上的。我们可以用一个滚动数组来求解问题:
空间优化后的代码:
//LCS空间优化
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int len,dp[2][5005],cur,pre;
string s1,s2;
int main(){
scanf("%d",&len);
cin >> s1;
s2 = s1;
reverse(s1.begin(), s1.end());
cur = 1, pre = 0;
for(int i = 1; i <= len; ++i){
for(int j = 1; j <= len; ++j)
if(s1[i - 1] == s2[j - 1]) dp[cur][j] = dp[pre][j - 1] + 1;
else dp[cur][j] = max(dp[pre][j], dp[cur][j - 1]);
swap(cur,pre);
}
printf("%d",len - dp[pre][len]);//注意这里必须是pre!因为做完之后会把pre和cur交换
return 0;
}
C.Compromise POJ - 2250
LCS打印序列~ 只是把单词当做整体,实质就是LCS. 可以采用dfs来输出,注意只有当
ans == dp[x - 1][y - 1] + 1 && ans == dp[x - 1][y] + 1 && ans == dp[x][y - 1] + 1才算确定的找到~
p.s.本题多组输入哦.
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dp[105][105],len1,len2,cnt;
string str1[105],str2[105],s,road[105];
void dfs(int ans, int x, int y){
if(!x || !y) return ;
if(ans == dp[x - 1][y - 1] + 1 && ans == dp[x - 1][y] + 1 && ans == dp[x][y - 1] + 1) road[ans] = str1[x], dfs(ans - 1,x - 1, y - 1);
else if(ans == dp[x - 1][y]) dfs(ans,x - 1, y);
else if(ans == dp[x][y - 1]) dfs(ans, x, y - 1);
}
int main(){
while(cin >> s){
len1 = len2 = 0;
while(s != "#"){
str1[++len1] = s;
cin >> s;
}
cin >> s;
while(s != "#"){
str2[++len2] = s;
cin >> s;
}
memset(dp,0,sizeof(dp));
for(int i = 1; i <= len1; ++i){
for(int j = 1; j <= len2; ++j){
if(str1[i] == str2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
}
}
dfs(dp[len1][len2],len1,len2);
cout << road[1];
for(int i = 2; i <= dp[len1][len2]; ++i)
cout << " " << road[i];
putchar('\n');
}
return 0;
}