动态规划——最长公共子序列(LCS)

暴力搜索枚举法

  1. 枚举序列X里的每一个子序列xi
  2. 检查子序列xi是否也是Y序列里的子序列;
  3. 在每一步记录当前找到的子序列里面的最长的子序列。
#include
#include
#include 
#include
using namespace std;
int main(){
string x,y; // 两个待比较的字符串
string seq;//最长公共子序列
string tmp;//临时变量
int len=0;//最长公共子序列长度
cin>>x>>y;
for(int i=0;i=0;j--)
     {
        if((i>>j)&1==1)
        {
            tmp = tmp + x[x.strlen()-1-j];
        }
     }
     int t=0,k=0;
     // 判断x的该子序列是否出现在y中
     while(k

在第1步枚举X中所有的子序列有2^m个,
每个子序列在Y中检查时间复杂度为O(n)。
因此蛮力策略的最坏时间复杂度为O(n*2^m),
这是指数级算法,对较长的序列求LCS是不适用的。
https://blog.csdn.net/qq_55553431/article/details/115189524

动态规划方法


X=
Y=为两个序列,
Z=是他们的任意公共子序列

经过分析,我们可以知道:

1、如果xm = yn,则zk = xm = yn 且 Zk-1是Xm-1和Yn-1的一个LCS

2、如果xm != yn 且 zk != xm,则Z是Xm-1和Y的一个LCS

3、如果xm != yn 且 zk != yn,则Z是X和Yn-1的一个LCS

用一个二维数组c表示
字符串X前i和Y前j个字符的LCS的长度,
可以得到以下公式:

动态规划——最长公共子序列(LCS)_第1张图片
打表法:

只需要从c[0][0]开始填表,填到c[m-1][n-1],所得到的c[m-1][n-1]就是LCS的长度

例如,ABCB和BDCA两个字符串的最大公共子序列长度为2,打表计算的过程如下所示。
动态规划——最长公共子序列(LCS)_第2张图片
动态规划——最长公共子序列(LCS)_第3张图片
动态规划——最长公共子序列(LCS)_第4张图片
动态规划——最长公共子序列(LCS)_第5张图片
动态规划——最长公共子序列(LCS)_第6张图片
动态规划——最长公共子序列(LCS)_第7张图片
右下角的2即为LCS的长度

由于只需要填一个m行n列的二维数组,
其中m代表第一个字符串长度,n代表第二个字符串长度。
时间复杂度为O(m*n)

获得最长公共子序列的方法:

X、Y两个字符串,
用一个二维数组b,
在对应字符相等的时候,用↖标记;
对应字符不相等,
在f(i-1,j)>=f(i,j-1)的时候,用↑标记;
在f(i-1,j) 比如说求ABCBDAB和BDCABA的LCS:
动态规划——最长公共子序列(LCS)_第8张图片
若想得到LCS,则再遍历一次b数组就好了,从最后一个位置开始往前遍历:
如果箭头是↖,则代表这个字符是LCS的一员,存下来后 i-- , j–;
如果箭头是←,则代表这个字符不是LCS的一员,j–;
如果箭头是↑ ,也代表这个字符不是LCS的一员,i–。
如此直到i = 0或者j = 0时停止,最后存下来的字符就是所有的LCS字符。

#include 
#include 
#include 
using namespace std;
void LCS(string s1,string s2)
{
    int m=s1.length()+1;
    int n=s2.length()+1;// 比字符串长度应该多出1
    //c[i][j] 表示字符串X前i和Y前j个字符的LCS的长度
     //b[i][j] 表示字符串的前进方向
    int **c;
    int **b;
    c=new int* [m];// b、c两个矩阵都有m行
    b=new int* [m];
    for(int i=0;i=c[i+1][j])
            {
                c[i+1][j+1]=c[i][j+1];
                b[i+1][j+1]=2;          //2表示箭头向  上
            }
            else
            {
                c[i+1][j+1]=c[i+1][j];
                b[i+1][j+1]=3;          //3表示箭头向  左
            }
        }
    }
    
    for(int i=0;i same;                   //存LCS字符
    stack same1,same2;             //存LCS字符在字符串1和字符串2中对应的下标,方便显示出来
    for(int i = m-1,j = n-1;i >= 0 && j >= 0; )
    {
        if(b[i][j] == 1)
        {
            i--;
            j--;
            same.push(s1[i]);
            same1.push(i);
            same2.push(j);
        }
        else if(b[i][j] == 2)
                i--;
             else
                j--;
    }
    
    cout<

代码的运行效果,如图所示。
动态规划——最长公共子序列(LCS)_第9张图片

记忆化搜索法:

#include
#include
#include
#define N 255
using namespace std;
const int c[N][N];
stack seq;
int LCS(string x,string y,int i,int j)
{
   if(j==0||i==0) return c[i][j]=0;
   if(c[i][j]!=-1)
   {
      return c[i][j];
   }
   if(x[i]==y[j])
   {
      c[i][j]=c[i-1][j-1]+1;
      seq.push(c[i][j]);
   }
   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[i][j];
}
int main()
{
   string x,y;
   cin>>x>>y;
   memset(c,-1,sizeof(c));
   cout<

你可能感兴趣的:(动态规划,算法)