两个字符串S1与S2,求出其最长的公共子序列?
序列不同于子串,序列可以中间不连续,只要满足前后关系。
例如123456与13579,它们有最长的公共子序列135.
// 第一行 输入S1
// 第二行输入S2
动态规划解法
思路:
采用动态规划的解法,还是先制作一个二维表,找出其递推关系。
从表中可分析出:如果S1[i]=S2[j],则dp[i][j]=dp[i-1][j-1]+1;如果S1[i]!=S2[j],则dp[i][j]=max(dp[i-1][j],dp[i][j-1]),此即为状态转移方程。
但是此表只能计算出LCS的长度,要输出LCS串,我们还需要从右下角向左上依次找出相同字符,可能不止一个,此例中只找出一种。
代码:
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
string s1,s2;
const int maxN=100;
int dp[maxN][maxN] ;
int main(){
string res;
int i,j,n1,n2;
cin>>s1;
cin>>s2;
n1=s1.size();
n2=s2.size();
for(i=0;i<n1;i++){
dp[i][0]=0;
}
for(j=0;j<n2;j++){
dp[0][j]=0;
}
for(i=1;i<=n1;i++) {
for(j=1;j<=n2;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]);
}
}
}
//到这里,只是求出LCS的长度,还要根据dp数组把串给找出来。
cout<<dp[n1][n2]<<endl;//需要时可输出LCS长度值。
j=n2;//j值提前赋值,大循环中要用
for(i=n1;i>0;i--) { //从最右下角 开始
if(dp[i][j]==dp[i-1][j])continue;
//如果相等继续找上一行,找到不相等,继续在本行向左找
for(;j>0;j--){//j值使用前值,所以第一个分句不赋初值。
if(dp[i][j-1]!=dp[i][j])break;
}//如果不相等,找到关键点,退出 ;如果相等,继续向左找
res=s1[i-1]+res;//取字符,插入前面
j--;//内循环提前结束,j要手动减1
}
cout<<res<<endl;
return 0;
}
深搜解法
思路:从S1中依次取出一个字符,与S2中从头到尾的字符比较,如果找到一个相同的,就把这个字符加到结果串中,然后再使用递归找两个剩余串的Lcs,再加到结果中。这时内循环就要退出,因为已使用递归了,不用再继续了。内循环结果后,找到一个结果,但要与当前最大公共子序列比较,只有超过它,才能取代它。如果找不到,结果就是空串。
#include <string>
#include <iostream>
using namespace std;
string s1,s2;
string dfs(string ss1,string ss2){
int n1=ss1.size();
int n2=ss2.size();
int i,j;
string maxans;
string ans;
for(i=0;i<n1;i++){
ans="";
for(j=0;j<n2;j++){
if(ss1[i]==ss2[j]){//如果找到一个公共字符
ans=ans+ss1[i]//就把它加到结果中,再找两个剩余串的LCS
+dfs(ss1.substr(i+1,n1-i-1),ss2.substr(j+1,n2-j-1));
break;//内循环结束
}
}
if(ans.size()>maxans.size())maxans=ans;
}
return maxans;
}
int main(){
string re;
cin>>s1;
cin>>s2;
re=dfs(s1,s2);
cout<<re;
return 0;
}