字符串-最长公共子序列(LCS)问题

题目:求两个字符串的最大公共子序列(可以不连续)的长度,并输出这个子序列。

例如:

输入 googleg和elgoog 输出 goog 4
输入 abcda和adcba 输出 aba 3

这里写图片描述
这里写图片描述

而为了得到相同的子序列,需要根据回溯路径,写出匹配字串,如下图,绿色的部分就是路径回溯的结果。
这里写图片描述

代码如下:

int SearchMaxLengthSequence(string s1, string s2)
{
    int length1 = s1.length();
    int length2 = s2.length();
    int** matrix = new int*[length1 + 1];
    for (size_t i = 0; i < length1 + 1; i++)
    {
        matrix[i] = new int[length2 + 1]();
    }

    for (size_t i = 1; i <= length1; i++)
    {
        for (size_t j = 1; j <= length2; j++)
        {
            if (s1[i - 1] == s2[j - 1])
            {
                matrix[i][j] = matrix[i - 1][j - 1] + 1;
            }
            else
            {
                matrix[i][j] = max(matrix[i][j - 1], matrix[i - 1][j]);
            }
        }
    }

    int commomLength = matrix[length1][length2];//最大子序列的长度
    string commomStr = "";//保存逆序的子序列
    //回溯部分
    while (length1 != 0)
    {
        if (matrix[length1][length2] == matrix[length1 - 1][length2])
        {
            length1--;
            continue;
        }
        if (matrix[length1][length2] == matrix[length1][length2 - 1])
        {
            length2--;
            continue;
        }
        if (matrix[length1][length2] == matrix[length1 - 1][length2 - 1] + 1)
        {
            commomStr += s1[length1 - 1];
            length1--;
            length2--;
        }
    }
    reverse(commomStr.begin(), commomStr.end());
    cout << commomStr << endl;
    return commomLength;
}

扩展:求字符串中回文字符串的个数

形式和前面那个类似,关键问题就是转移矩阵的确定:
 定义dp [i][j]:表示字符串序列A的前i个字符组成的序列Ax和字符串序列B的前j个字符组成的序列By之间的公共子序列的个数(m ,n分别为Ax和By的长度,i<=m,j<=n)

状态转移方程:
if Ax[i] != By[j] 则有 dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]
else dp[i][j]=dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]+dp[i+1][j-1]+1=dp[i+1][j] + dp[i][j-1]+1
其中序列str:[0][1][2]……[i][i+1]…….[j-1][j]……
代码:

int NumOfSubPlalindrome(string str)
{
    int len = str.length();

    if (len == 0)
        return 0;


    vector<vector<int> > dp(len, vector<int>(len));//对此只进行了一半的使用

    for (int j = 0; j < len; j++)
    {
        dp[j][j] = 1;
        for (int i = j - 1; i >= 0; i--)
        {
            dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1];
            if (str[i] == str[j])
            {
                dp[i][j] += dp[i + 1][j - 1] + 1;
            }
        }
    }

    return dp[0][len - 1];//表示从0至len-1之间的回文个数
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值