LCS
题目描述:
给定一个字符串 s s s 和一个字符串 t t t ,输出 s s s 和 t t t 的最长公共子序列。
输入格式:
两行,第一行输入 s s s ,第二行输入 t t t 。
输出格式:
输出 s s s 和 t t t 的最长公共子序列。如果有多种答案,输出任何一个都可以。
说明/提示:
数据保证 s s s 和 t t t 仅含英文小写字母,并且 s s s 和 t t t 的长度小于等于3000。
样例 #1
样例输入 #1
axyb
abyxb
样例输出 #1
axb
样例 #2
样例输入 #2
aa
xayaz
样例输出 #2
aa
样例 #3
样例输入 #3
a
z
样例输出 #3
样例 #4
样例输入 #4
abracadabra
avadakedavra
样例输出 #4
aaadara
思路
看到这道题目题干狂喜,我对着题目嘿嘿嘿,看到这个数据范围,题目对着我嘿嘿嘿……
我想着,要不暴力先试试,说不定就过了呢?
这道题主要考察动态规划,写出转移方程即可:
我们虚拟两个分别指向字符串最前面的指针,然后逐一往后去比较
设 s 1 s1 s1字符串的长度记为 ∣ s 1 ∣ |s1| ∣s1∣
$ 当s1[i]=s2[j]时,ret[i][j]=ret[i-1][j-1]+s1[i-1] $
当 s 1 [ i ] = s 2 [ j ] 时 : 当s1[i]=s2[j]时: 当s1[i]=s2[j]时:
如 果 ∣ r e t [ i − 1 ] [ j ] ∣ > ∣ r e t [ i ] [ j − 1 ] ∣ , r e t [ i ] [ j ] = r e t [ i − 1 ] [ j ] ; 如果|ret[i-1][j]|>|ret[i][j-1]|,ret[i][j]=ret[i-1][j]; 如果∣ret[i−1][j]∣>∣ret[i][j−1]∣,ret[i][j]=ret[i−1][j];
如 果 ∣ r e t [ i − 1 ] [ j ] ∣ < = ∣ r e t [ i ] [ j − 1 ] ∣ , r e t [ i ] [ j ] = r e t [ i ] [ j − 1 ] ; 如果|ret[i-1][j]|<=|ret[i][j-1]|,ret[i][j]=ret[i][j-1]; 如果∣ret[i−1][j]∣<=∣ret[i][j−1]∣,ret[i][j]=ret[i][j−1];
当我定义了一个$ 3005 *3005 $的string数组的时候,直接段错误(╥﹏╥)
就想着能不能从数组的大小那里优化.
然后我看向了滚动数组~~(滚动数组:不要啊……~~
感觉肯定能过了,然后超时(啪~)
分析思路后,感觉没有问题,后来发现自己的代码实现还可以优化,之前的部分如下:
string Max(string s1,string s2)
{
return s1.size()>s2.size()?s1:s2;
}
以及
for(int i=1;i<=s1.size();i++){
for(int j=1;j<=s2.size();j++){
if(s1[i-1]==s2[j-1]){
ret[i&1][j]=ret[(i-1)&1][j-1]+s1[i-1];
}else {
ret[i&1][j]=Max(ret[(i-1)&1][j],ret[i&1][j-1]);
}
}
}
虽然说封装是个好习惯,但是恰恰是封装成函数,使得这次翻车了……
我掐指一算,发现最坏的情况,调用函数太多导致时间效率过差.
于是我又改进了一下,得到了一下的AC代码.
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
string Max(string s1,string s2)
{
return s1.size()>s2.size()?s1:s2;
}
int main()
{
string s1,s2;
string ret[2][maxn]={};
cin>>s1>>s2;
for(int i=1;i<=s1.size();i++){
for(int j=1;j<=s2.size();j++){
if(s1[i-1]==s2[j-1]){
ret[i&1][j]=ret[(i-1)&1][j-1]+s1[i-1];
}else {
ret[i&1][j]=(ret[(i-1)&1][j].size()>ret[i&1][j-1].size()?ret[(i-1)&1][j]:ret[i&1][j-1]);
}
}
}
cout<<Max(ret[0][s2.size()],ret[1][s2.size()])<<endl;
return 0;
}
收获
1、LCS的状态转移方程
2、滚动数组在动态规划的应用
3、自己封装的函数要注意时间的优化,感觉不妙就去掉函数.这条规则同样适用于头文件引用的库函数