最长公共子序列问题

302 阅读2分钟

这是我参与 8 月更文挑战的第 18 天,活动详情查看: 8月更文挑战

定义:

设序列M是序列A(长度为n)和序列B(长度为m)的子序列,且是所有符合同时是序列A和序列B中的子序列中最长的子序列,则序列M就是我们要求解的最长子序列。

应用:

对比两个文章的相似程度。

方法

方法一、枚举法

A有2n个子序列(n个数只有两种选择:取或不取),对每个子序列确定是否为最长的子序列,时间复杂度为O(m2n)

方法二、动态规划dp

令L[i,j]表示Ai和Bj的最长公共子序列长度,则

WechatIMG170.jpeg 设计一个字符串数组,用来保存最长公共子序列,两个整型数组,保存最长公共子序列在字符串A和字符串B中对应的下标.

(1)C++中定义二维数组

    //申请空间
    int** a2 = new int*[rows];
    for(int i=0;i<rows;i++)
        a2[i] = new int[columns];
    //释放空间
    for(int i=0;i<rows;i++)
        delete []a2[i];
    delete []a2;
#include<iostream>
#include<string>
#include<stack>
using namespace std;
//生成m行n列的二维数组
void LCS(string s1,string s2)
{
    int m=s1.length()+1;
    int n=s2.length()+1;
    int **c;
    int **b;
    c=new int* [m];
    b=new int* [m];
    for(int i=0;i<m;i++){
        c[i]=new int[n];
        b[i]=new int[n];
        for(int j=0;j<n;j++)
        b[i][j]=0; 
    }
    //无论是第一个字符串长度还是第二个字符串长度为0,最长字符串一律为0
    for(int i=0;i<m;i++)
        c[i][0]=0;
    for(int i=0;i<n;i++)
        c[0][i]=0;
    for(int i=0;i<m-1;i++)
    {
        for(int j=0;j<n-1;j++)
        {
            if(s1[i]==s2[j])
            {
                c[i+1][j+1]=c[i][j]+1;
                b[i+1][j+1]=1;//用1表示箭头↖
            }
            else if(c[i][j+1]>=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<m;i++)
    {
        for(int j=0;j<n;j++)
        {
            cout<<c[i][j]<<' ';
        }
        cout<<endl;
    }

    stack<char> same;//存最长公共子序列
    stack<int> same1,same2; //存最长公共子序列在字符串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]);//或s2[j]
            same1.push(i);
            same2.push(j);
        }
        else if(b[i][j]==2)
            i--;
        else
        {
            j--;
        }
    }
        cout<<s1<<endl;//输出s1
        for(int i=0;i<m && !same1.empty();i++)//输出字符串1的标记
        {
            if(i==same1.top())
            {
                cout<<1;
                same1.pop();
            }
            else
            {
                cout<<' ';
            }
        }
        cout<<endl<<s2<<endl;
        for(int i=0;i<n && !same2.empty();i++)
        {
            if(i==same2.top())
            {
                cout<<1;
                same2.pop();
            }
            else
            {
                cout<<' ';
            } 
        }
           cout<<endl<<"最长公共子序列为:";
    while(!same.empty())
    {
        cout<<same.top();
        same.pop();
    }
    cout<<endl<<"长度为:"<<c[m-1][n-1]<<endl;
    for (int i = 0; i<m; i++)
    {
        delete [] c[i];
        delete [] b[i];
    }
    delete []c;
    delete []b;
}
int main()
{
    string s1="ABCPDSFJGODIHJOFDIUSHGD";
    string s2="OSDIHGKODGHBLKSJBHKAGHI";
    LCS(s1,s2);
    system("pause");
    return 0;
}