一、题目
求两个字符串的所有最长公共子序列。
输入格式:
输入长度≤100的两个字符串。
输出格式:
输出两个字符串的所有最长公共子序列,若最长公共子序列多于1个,则将所有子序列按字典序从小到大排序后输出。
二、代码
/*
描述:求解最长公共子序列
日期:221031
*/
#include <iostream>
#include <set>
using namespace std;
int dp[101][101] = {0};
set<string> res;
string s1, s2;
/*
如何获取最大子串长度?
1、dp[i][j]用于保存s1的前i个和s2的前j个的最大子串长度。
2、状态转移方程:
dp[i][j]=
(1)if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1
若是两个字母相等那么,说明可以再进一步,也即+1,也就是说,s1的前i-1个和s2的前j-1个
的最大长度可以再加上s1[i](s2[j])这个字母
(2)else dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
此时说明s1[i]!=s2[j],那么不能加上这个字母,那么不能加上又是多少呢?就看
s1的前i-1个和s2的前j个,s1的前i个和s2的前j-1个,这两个哪个大就选哪个,因为毕竟又多了
一个字母,肯定要在它们的基础上继承,所以继承一个更大的
*/
int max_len(int m, int n)
{
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (s1[i-1] == s2[j-1])
//这里有些绕,之所以用i-1和j-1,是因为在字符串中,是从0开始的,而dp数组中
//0行和0列没有意义,不代表字符串中的内容
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
void rec_save_str(int i,int j,string str)
{
while(i>0&&j>0)
{
if(s1[i-1]==s2[j-1])
//当两个字母相等的时候,那么双指针i,j同时减减,并且对str做头插入
//因为整个获取str的过程从后向前的
{
j--;
i--;
str=s1[i]+str;
}
else if(dp[i-1][j]<dp[i][j-1])
//意思是s1的前i-1个和s2的前j个的最大子串长度小于s1的前i个和s2的前j-1个
//说明s1[i]这个字母是重要的,一旦没了它,最大子串长度就要减去,所以此时不能动i,要动j
/*
以ABDBCAB,BDCAB举例子,从后往前走,CAB都是可以被保存下去的,所以我们就不管CAB了,
且i和j会一路向前,则字符串变为ABDB,BD,此时i和j都指向最后一个字母
dp[i-1][j]意思是:ABD和BD的最大子串长度,为2
dp[i][j-1]意思是:ABDB和B的最大子串长度,为1
这就说明,减去的那一个字母s2[j]是非常重要的,是子串的组成之一
所以就不能让j--,而是i--
如果上面的比较反过来,也就是本else if的情况,那么就说明s[i]很重要,是子串的组成之一
就不能让i--,而是j--
*/
{
j--;
}
else if (dp[i-1][j]>dp[i][j-1])
//同理上面
{
i--;
}
else
/*
此时的else其实只剩下一个可能性,dp[i-1][j]==dp[i][j-1],当两个数字相等的时候
举个例子为ABDC和ABCD,可以很轻松得出结果,最大子串长度为3,子串可以为ABD或ABC
dp[i-1][j]意思为:ABD和ABCD的最大子串长度,为3
dp[i][j-1]意思为:ABDC和ABC的最大子串长度,为3
二者相等,此时看我们的例子可知道,子串开始有不同解了,所以要进行递归,而且要传递str的值
因为str保存了相同后缀,举个例子ABDCEF和ABCDEF,最大长度为5
子串为ABDEF或ABCEF,可以知道,str在 dp[i-1][j]==dp[i][j-1]之前已经保存了EF两个字母,
他们也正是答案的相同后缀,所以要递归,让他们走不同的路
*/
{
rec_save_str(i-1,j,str);
rec_save_str(i,j-1,str);
return;
}
}
if(str.length()) res.insert(str);
}
int main()
{
cin >> s1 >> s2;
int m = s1.size();
int n = s2.size();
//生成dp矩阵
int son_len_max=max_len(m, n);
//生成结果
string str;
rec_save_str(m,n,str);
//输出结果
/*
这里选择set
1、是因为set可以自动排序
2、是因为在rec_save_str(m,n,str)过程中会有重复的子串插入,所以也做了去重
*/
set<string>::iterator it=res.begin();
if(res.empty())
cout<<"NO"<<endl;
else
{
for(;it!=res.end();it++)
cout<<*it<<endl;
}
return 0;
}