概念明晰:
- longest common sub-sequences:最长 - 公共 - 子 - 序列
- 子串:按原顺序依次出现,禁止跳过某元素
- 子序列:在保持元素前后关系的前提下,可以跳过某些元素的序列
例题:Common Subsequence(HDU 1159)
解题思路:
- 动态规划:
一般来说,动态规划分为五个部分:
1.状态转移方程
2.表格的初始化
3.确定循环顺序
4.复杂度的优化
5.记录抉择路径
优化过程:
- 对于dp问题,最快的优化方法是手推表格。表格能提供两个信息:dp[i][j]需要用到哪些格子,如何去用这些格子。
- “用到哪些”告诉我们需要保存几维信息,“如何去用”告诉我们方程的形式。比如本题的空间优化方法,不手推表格是很难想到的。
代码:
- 状态转移方程:
if(s[i] == t[j])
lcs[i][j] = lcs[i-1][j-1] + 1;
else
lcs[i][j] = max(lcs[i][j-1] , lcs[i-1][j]);
- 完整代码1(原始正方形dp:124MS 5356K)
//原始正方形dp:124MS 5356K
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int maxn = 1005;
string s,t;
int lcs[maxn][maxn];//lcs[i][j]代表两前缀s[1...i] & t[1...j]的LCS
int GetLCS(){
int nums = s.length();
int numt = t.length();
int i=1 , j=1;
string::iterator sp,tp;
memset(lcs,0,sizeof(lcs));
for(sp = s.begin() ; sp != s.end() ; i++,sp++){
j=1;
for(tp = t.begin() ; tp != t.end() ; j++,tp++){
if(*sp == *tp)
lcs[i][j] = lcs[i-1][j-1] + 1;
else
lcs[i][j] = max(lcs[i][j-1] , lcs[i-1][j]);
}
}
return lcs[nums][numt];
}
int main(){
while(cin>>s>>t){
int ans = GetLCS();
cout<<ans<<endl;
}
return 0;
}
- 完整代码2(一维dp:46MS 1408K)
//一维dp:46MS 1408K
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
const int maxn = 1005;
string s,t;
int lcs[maxn];
int rec[maxn];//record[i]记录需要用到的上一维的信息
int main(){
while(cin>>s>>t){
string::iterator sp , tp;
int i,j;
memset(lcs , 0 , sizeof(lcs));
memset(rec , 0 , sizeof(rec));
for(sp = s.begin(),i = 1 ; sp != s.end() ; sp++,i++){
for(tp = t.begin(),j = 1 ; tp != t.end() ; tp++,j++){
if(*sp == *tp)
lcs[j] = rec[j-1] + 1;
else
lcs[j] = max(rec[j] , lcs[j-1]);
}
if(sp+1 == s.end()) break;
while(j--)
rec[j] = lcs[j];
}
cout<<lcs[j-1]<<endl;
}
return 0;
}