本博客所有问题都可能有多种解法,但这里主要讲解其动态规划的算法
1、最长公共子序列
2、最长公共子串
3、最长递增子序列
最长公共子序列(LCS)
【问题描述】
最长公共子序列,英文缩写为LCS,其定义是,一个序列 S ,如果分别是序列(S1,S2……)的子序列,且是所有符合此条件
序列中最长的,则 S称为已知序列的最长公共子序列。需要注意的是序列S并不一定是序列(S1,S2……)中的连续序列。
【DP算法】
对字符串X=<x1, x2, ……,xm>,Y=<y1,y2,……,yn>,LCS(X,Y)表示X和Y的一个最长公共子序列,观察可得到如下
规则:
1、如果xm=yn,则LCS ( X,Y ) = 1+LCS ( Xm-1,Yn-1 )。
2、如果xm!=yn,则LCS( X,Y )=max{ LCS ( Xm-1, Y ), LCS ( X, Yn-1 ) }
【代码】
int LCS(string s1, string s2) {
intnLen1 = s1.length();
intnLen2 = s2.length();
vector<vector<int>>c;
c.resize(nLen1+ 1);
for(inti = 0; i <= nLen1; i++)
c[i].resize(nLen2+ 1, 0);
for(int i = 1; i <= nLen1; i++) {
for(int j = 1; j < nLen2; j++) {
if(s1[i - 1] == s2[j - 1])
c[i][j]= c[i - 1][j - 1] + 1;
else{
if (c[i - 1][j]> c[i][j - 1])
c[i][j]= c[i - 1][j] ;
else
c[i][j]= c[i][j - 1] ;
}
}
}
return c[nLen1][nLen2];
}
最长公共子串
【问题描述】
最长公共子串与最长公共子序列唯一的不同是要求序列S是字符串S1,S2等的连续序列
【DP算法】
其DP思想与LCS十分相似,只需在状态转移方程上稍作修改
【代码】
int LC_continue_S(string s1, string s2) {
intnLen1 = s1.length();
intnLen2 = s2.length();
vector<vector<int>>c;
c.resize(nLen1+ 1);
for(inti = 0; i <= nLen1; i++)
c[i].resize(nLen2+ 1, 0);
int nMax= 0;
for(int i = 1; i <= nLen1; i++) {
for(int j = 1; j < nLen2; j++) {
if(s1[i - 1] == s2[j - 1]) {
c[i][j]= c[i - 1][j - 1] + 1;
if(c[i][j] > nMax)
nMax= c[i][j];
}
else
c[i][j]= 0;
}
}
return nMax;
}
【问题描述】
给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)
【DP算法】
解法1:转化为最长公共子序列问题
将数组arr[]从小到大排序得到sorted_arr[],然后找出最长公共子序列,显然得到的结果亦是最长递增子序列,这里我们不再讲解
解法2:
像LCS一样,从后向前分析,很容易想到,第i个元素之前的最长递增子序列的长度要么是1(单独成一个序列),要么就是第i-1
个元素之前的最长递增子序列加1,可以有状态方程:
LIS[i] = max{1,LIS[k]+1},其中,对于任意的k<=i-1,arr[i] >arr[k]
这样arr[i]才能在arr[k]的基础上构成一个新的递增子序列。
解法3:二分查找,这里不提供说明,请跳转大神博客点击打开链接
【代码】
解法2代码
int LIS(int arr[],int size) {
int lis = 0;
for(int i = 0; i < size; ++i) {
dp[i] = 1;
for(int j = 0; j < i; ++j) {
if(arr[i] > arr[j] && dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1;
if(dp[i] > lis)
lis = dp[i];
}
}
}
return lis;
}