最长公共子序列问题(LCS)之动态规划法

题目描述
使用动态规划算法求解两个序列的最长公共子序列的长度。
输入
每组输入包括两行,每行包括一个字符串。
输出
两个序列的最长公共子序列的长度。
样例输入
ACBCDABD
ABDCABA
样例输出
5

ps:

100个动态规划方程

这是一个典型的求解最长公共子序列的题目,只要找到公式,问题就迎刃而解了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int a[maxn], dp[1300][1300];
char c[110];
string s1, s2;
int lcs()
{
   
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= s1.length(); ++i)
        for (int j = 1; j <= s2.length(); ++j)
        {
   
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
}
int main()
{
   
    while (cin >> s1 >> s2)
    {
   
        lcs();
        printf("%d\n", dp[s1.length()][s2.length()]);
    }
    return 0;
}

当然也可以把函数写在主函数里面,这就要看读者的爱好了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int a[maxn], dp[1300][1300];
char c[110];
string s1, s2;
int lcs()
{
   
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= s1.length(); ++i)
        for (int j = 1; j <= s2.length(); ++j)
        {
   
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    return dp[s1.length()][s2.length()];
}
int main()
{
   
    while (cin >> s1 >> s2)
    {
   
        printf("%d\n", lcs());
    }
    return 0;
}

接下来就上正菜了!

最长公共子序列问题(LCS)-构造LCS

题目描述
使用动态规划算法求两个序列的最长公共子序列,需构造一条最长公共子序列。
输入
每组输入包括两行,每行包括一个字符串。
输出
两个字符序列的一条最长公共子序列。(输入已确保最长公共子序列的唯一性)
样例输入
acdbxx
ccdxx
样例输出
cdxx

我们知道有时候最长公共子序列不唯一,这个题目是在保证唯一的情况下构造lcs,附上晖队的的讲解


拐角处就是匹配的位置,只要从最高层每次从拐角走到下一层…一直下到0的那一层,经过的拐角处的字
符连在一起就是一个最长公共子序列(不过是逆序的)
只要找到一种走法就行
可以从右下角开始,先向上走走到和下一层的边界,再向左走走到和下一层的边界,所在的位置便是一
个拐角
然后向左上走到下一层,继续上述操作,直到走到第0层

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
char a[maxn], b[maxn], c[maxn];
int dp[maxn][maxn];
int main()
{
   
    while (~scanf("%s %s", a + 1, b + 1)) //输入从数组下标为1开始
    {
                                        //不管怎样都要先计算出最长公共子序列的长度
        int la = strlen(a + 1);
        int lb = strlen(b + 1);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= la; ++i)
        {
   
            for (int j = 1; j <= lb; ++j)
            {
   
                if (a[i] == b[j])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        int xx = la, yy = lb, cnt = dp[la][lb];
        c[cnt] = '\0'; //字符串的结束标志,需要自己手动补上,不然程序不知道什么时候才会截止
        while (dp[xx][yy])
        {
   
            if (dp[xx - 1][yy] >= dp[xx][yy - 1]) //如果上面那个大于左边那个
            {
   
                if (dp[xx - 1][yy] < dp[xx][yy]) //如果上面的小于长度那么就等于找到了
                {
   
                    c[--cnt] = a[xx]; //赋值
                    --yy;             //那就往左边找
                }
                else
                    --xx; //否则就往上面找
            }
            else
            {
   
                if (dp[xx][yy - 1] < dp[xx][yy])
                {
   
                    c[--cnt] = a[xx];
                    --xx;
                }
                else
                    --yy;
            }
        }
        printf("%s\n", c);
    }
}
// int xx = la, yy = lb, cnt = dp[la][lb];
// c[cnt] = '\0'; //补上字符串结束标志
// while (dp[xx][yy])
// {
   
// while (dp[xx - 1][yy] == dp[xx][yy] && xx > 1)
// { //向上走 xx>1避免出界
// --xx;
// }
// while (dp[xx][yy - 1] == dp[xx][yy] && yy > 1)
// { //向左走 yy>1避免出界
// --yy;
// }
// c[--cnt] = a[xx];
// --xx, --yy;
// }

递归代码
主要思想就是标记法+递归

#include <bits/stdc++.h>
using namespace std;
int b[1005][1005], dp[1005][1005];
char str1[1005], str2[1005];
int LCSlength()
{
   
    int ls1 = strlen(str1 + 1);
    int ls2 = strlen(str2 + 1);
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= ls1; ++i)
        for (int j = 1; j <= ls2; ++j)
        {
   
            if (str1[i] == str2[j])
            {
   
                dp[i][j] = dp[i - 1][j - 1] + 1;
                b[i][j] = 1;
            }
            else if (dp[i - 1][j] >= dp[i][j - 1])
            {
   
                dp[i][j] = dp[i - 1][j];
                b[i][j] = 2;
            }
            else
            {
   
                dp[i][j] = dp[i][j - 1];
                b[i][j] = 3;
            }
        }
    return dp[ls1][ls2];
}
void LCS(int i, int j)
{
   
    if (i == 0 || j == 0)
        return;
    if (b[i][j] == 1)
    {
   
        LCS(i - 1, j - 1);
        printf("%c", str1[i]);
    }
    else if (b[i][j] == 2)
        LCS(i - 1, j);
    else
        LCS(i, j - 1);
}
int main()
{
   
    while (~scanf("%s %s", str1 + 1, str2 + 1))
    {
   
        LCSlength();
        LCS(strlen(str1 + 1), strlen(str2 + 1));
    }
    return 0;
}
生活总是苦乐交织的,但是心态和努力可以改变现状