最长公共子序列

24 篇文章 0 订阅

题目: 我们有两个字符串m和n,如果它们的子串a和b内容相同,则称a和b是m和n的公共子序列。子串中的字符不一定在原字符串中连续。
例如字符串“abcfbc”和“abfcab”,其中“abc”同时出现在两个字符串中,因此“abc”是它们的公共子序列。此外,“ab”、“af”等都是它们的字串。
现在给你两个任意字符串(不包含空格),请帮忙计算它们的最长公共子序列的长度。

示例1:

输入 :
abcfbc abfcab
programming contest
abcd mnp


输出:
4
2
0

动态规划思想
建立一个tmp矩阵,是两个字符串一个纵对应,一个横向对应。

tmp[i][j] 表示str1[0…i]和str2[0…j]的最长公共子序列的长度
求解 tmp[i][j]为:

  • 如果str1[i] == str2[j],则 tmp[i][j] = max( tmp[i-1][j], tmp[i][j-1], tmp[i-1][j-1]+1 )
  • 如果str1[i] != str2[j],则 tmp[i][j] = max( tmp[i-1][j], tmp[i][j-1] )

注意:

因为状态转移方程用到了上一行或者前一列的转态, 所以在初始化时, 给记录转态的tmp矩阵 第0行第0列进行了赋0操作,
真正字符串的转移状态从(1,1)开始到(str1.size(), str2.size())结束
而为了给tmp矩阵第0行第0列 赋 0 方便, 这里我直接初始化tmp 为全0阵

最后返回tmp[M][N],M、N为两个字符串的长度

举例:
序列s1 = { A,B,C,B,D,A,B}
序列s2 = { B,D,C,A,B,A}
最终结果{B C B A}
在这里插入图片描述

// 最长公共子序列
#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main() {
    string s1, s2;
    while (cin >> s1 >> s2) {
        int res = 0;
        // 为了方便给tmp矩阵第0行0列赋0值, 
        // 初始化tmp 为全零矩阵
        vector<vector<int>> tmp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
        for (int i = 1; i <= s1.size(); ++i) {
            for (int j = 1; j <= s2.size(); ++j) {
                if (s1[i - 1] == s2[j - 1]) {
                    tmp[i][j] = tmp[i - 1][j - 1] + 1;
                }
                else if (tmp[i - 1][j] > tmp[i][j - 1])
                    tmp[i][j] = tmp[i - 1][j];
                else
                    tmp[i][j] = tmp[i][j - 1];
            }
        }
        cout << tmp[s1.size()][s2.size()] << endl;
    }
    return 0;
}

如果需要输出最长子序列: 则对代码稍作修改, 添加一个 temp矩阵.
temp[i][j] 用来记录 tmp[i][j] 是通过哪个方向求得的.

// 最长公共子序列
#include<iostream>
#include<string>
#include<vector>
using namespace std;

void Construct(vector<vector<int>> temp, string s1, int i, int j) {
    if (i == 0 || j == 0)
        return;
    if (temp[i][j] == 0) {
        Construct(temp, s1, i - 1, j - 1);
        cout << s1[i - 1];
    }
    else if (temp[i][j] == 1)
        Construct(temp, s1, i - 1, j);
    else
        Construct(temp, s1, i, j - 1);
}

int main() {
    string s1, s2;

    while (cin >> s1 >> s2) {
        int res = 0;
        vector<vector<int>> tmp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
        vector<vector<int>> temp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
        for (int i = 1; i <= s1.size(); ++i) {
            for (int j = 1; j <= s2.size(); ++j) {
                if (s1[i - 1] == s2[j - 1]) {
                    tmp[i][j] = tmp[i - 1][j - 1] + 1;
                    temp[i][j] = 0;
                }
                else if (tmp[i - 1][j] > tmp[i][j - 1]) {
                    tmp[i][j] = tmp[i - 1][j];
                    temp[i][j] = 1;
                }
                else {
                    tmp[i][j] = tmp[i][j - 1];
                    temp[i][j] = -1;
                }
            }
        }
        Construct(temp, s1, s1.size(), s2.size());
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值