问题
给定两个字符串,例如"sadstory"和"admisory",求出二者的最长公共子序列的长度;
解决思路
1.暴力法
1.设a,b长度分别为n,m;对于每个字符,有选或不选;所以共有2^(n+m)个情况;
2.在得到两个子序列后,比较两个子序列是否一样;
这个办法的时间复杂度为(O(2^(n+m) * max(n,m)));很费时间,因此考虑动态规划解法;
暴力法代码展示:
#include<bits/stdc++.h>
using namespace std;
/*
sadstory
adminsorry
*/
char s1[100],s2[100];
int dp[100][100];
int l1,l2;
int ans=0;
void DFS(int x,int y,string f1,string f2){
if(x>l1||y>l2){
if(f1==f2){
int l=f1.size();
ans=max(ans,l);
}
return;
}
for(int i=x;i<=l1;i++){
for(int j=y;j<=l2;j++){
DFS(i+1,j,f1+s1[i],f2);
DFS(i,j+1,f1,f2+s2[j]);
DFS(i+1,j+1,f1+s1[i],f2+s2[j]);
// DFS(i,j,f1+s1[i],f2);
}
}
}
int main(){
while(true){
gets(s1+1);
gets(s2+1);
l1=strlen(s1+1);
l2=strlen(s2+1);
string f1="",f2="";
DFS(1,1,f1,f2);
cout<<ans<<endl;
}
return 0;
}
2.动态规划
1.用dp[i][j]表示串a的前i个字符与b的前j个字符中的LCS长度;
2.当a[i]==b[j]时,有dp[i][j]=dp[i-1][j-1]+1;
3.当a[i]!=b[j]时,有dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
代码展示
#include<bits/stdc++.h>
using namespace std;
/*
sadstory
adminsorry
*/
char s1[100],s2[100];
int dp[100][100];
int main(){
while(true){
gets(s1+1);
gets(s2+1);
int l1=strlen(s1+1);
int l2=strlen(s2+1);
//cout<<s1+1<<" "<<s2+1<<endl;
memset(dp,0,sizeof(dp));
for(int i=1;i<=l1;i++){
for(int j=1;j<=l2;j++){
if(s1[i]==s2[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
// cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
}
}
cout<<dp[l1][l2]<<endl;
}
return 0;
}
总结
例题
https://blog.csdn.net/alpha_xia/article/details/114677569
拓展
我们已经求出了最长公共子串的长度,那我们如何求出公共子串的序列呢?
- 根据上面的动态规划解法,我们可以知道这个方法就是存储一张表,记录该节点是否是公共子序列的一部分;
是:标记为1
不是:判断a[0~i]与b[0~j]这部分的公共子序列应该往哪个方向去找:
dp[i-1][j]>dp[i][j-1] 往上找
dp[i][j-1]>dp[i-1][j] 往左找
代码如下
void get_lcs(int l1,int l2){
string s="";
while(l1&&l2){
if(flag[l1][l2]==1)
s+=s1[l1],l1--,l2--;
else if(flag[l1][l2]==2)
l1--;
else l2--;
}
reverse(s.begin(),s.end());
cout<<s<<endl;
}