ACM基础之动态规划DP:LCS最长公共子序列和最长子串


一、最长公共子序列

1.分析

在这里插入图片描述

2.伪码

在这里插入图片描述

// X,Y是字符串
LCS-LENGTH(X, Y):
	m ← length[X]
	n ← length[Y]
	// 初始化边界,用于下面开始i=0时的c[i-1,j-1]之类的获取0值
	// 初始化列为0,空过c[0,0]
	for i ← 1 to m
		do	c[i,0]0
	// 初始化行为0,顺便填上c[0,0]
	for j ← 0 to n
		do	c[0,j]0
	
	// 先行后列,是逐行的效果
	// 每行
	for i ← 1 to m
			// 每列
		do	for j ← 1 to n
					// 一样字符,看左上角↖,其值+1
				do	if x[i] = y[j]
					then	c[i,j] ← c[i-1,j-1] + 1
							// 左上箭头是因为c[i-1,j-1]是在c[i,j]左上角
							b[i,j]"↖"
					// 不一样的字符,比较上方↑和左方←,看谁大取谁
					else	if c[i-1,j] ≥ c[i,j-1]
							then	c[i,j] ← c[i-1,j]
									// 因为c[i-1,j]是在c[i,j]上一行
									b[i,j]"↑"
							else	c[i,j] ← c[i,j-1]
									// 因为c[i,j-1]是在c[i,j]左一列
									b[i,j]"←"

在这里插入图片描述

PRINT-LCS(b, X, i, j)
	// 第一行、第一列时
	if i = 0 or j = 0
		then return
	// 左上,表示相等字符才输出
	if b[i,j] = "↖"
	then	PRINT-LCS(b,X,i-1,j-1)
			print x[i]
	// 上
	else if	b[i,j] = "↑"
		 then PRINT-LCS(b,X,i-1,j)
	// 左
	else PRINT-LCS(b,X,i,j-1)

3.C++

#include <iostream>
#include <vector>
#include <string.h>
using namespace std;

void LCS_LEGTH(char x[], char y[], vector<vector<int>> &c, vector<vector<char>> &b)
{
    int m = strlen(x);
    int n = strlen(y);

    // 初始化边界,用于下面开始i=0时的c[i-1,j-1]之类的获取0值
    // 初始化列为0,空过c[0,0]
    for (int i = 1; i <= m; i++)
    {
        c[i][0] = 0;
    }
    // 初始化行为0,顺便填上c[0,0]
    for (int j = 0; j <= n; j++)
    {
        c[0][j] = 0;
    }

    // 先行后列,是逐行的效果
    // 每行
    for (int i = 1; i <= m; i++)
    {
        // 每列
        for (int j = 1; j <= n; j++)
        {
            // 一样字符,看左上角↖,其值+1
            // 注意,x、y从0开始
            if (x[i - 1] == y[j - 1])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
                // 左上箭头是因为c[i-1,j-1]是在c[i,j]左上角
                // 用q表示左上,因为'↖'超过了char范围
                b[i][j] = 'q';
            }
            // 不一样的字符,比较上方↑和左方←,看谁大取谁
            else
            {
                if (c[i - 1][j] >= c[i][j - 1])
                {
                    c[i][j] = c[i - 1][j];
                    // 因为c[i-1,j]是在c[i,j]上一行
                    // 用w表示上
                    b[i][j] = 'w';
                }
                else
                {
                    c[i][j] = c[i][j - 1];
                    // 因为c[i,j-1]是在c[i,j]左一列
                    // 用a表示左
                    b[i][j] = 'a';
                }
            }
        }
    }
}

void print_LCS(vector<vector<char>> &b, char x[], int i, int j)
{
    // 第一行、第一列时
    if (i == 0 || j == 0)
    {
        return;
    }
    // 左上q,表示相等字符才输出
    if (b[i][j] == 'q')
    {
        print_LCS(b, x, i - 1, j - 1);
        // 注意,x、y从0开始
        printf("%c", x[i - 1]);
    }
    // 上w
    else if (b[i][j] == 'w')
    {
        print_LCS(b, x, i - 1, j);
    }
    // 左a
    else
    {
        print_LCS(b, x, i, j - 1);
    }
}
int main(void)
{
    char x[] = "ABCBDAB";
    char y[] = "BDCABA";
    int m = strlen(x);
    int n = strlen(y);
    // m+1行,n+1列,因为要特别使用第一行第一列,[0,m],[0,n]
    vector<vector<int>> c(m + 1, vector<int>(n + 1));
    vector<vector<char>> b(m + 1, vector<char>(n + 1));
    // 解题
    LCS_LEGTH(x, y, c, b);
    // 输出
    print_LCS(b, x, m, n);
    return 0;
}

二、最长子串

1.分析

在这里插入图片描述

2.c++

#include <iostream>
#include <vector>
#include <string.h>
using namespace std;

void LCS_substring(char x[], char y[])
{
    int m = strlen(x);
    int n = strlen(y);
    // m+1行,n+1列,因为要特别使用第一行第一列,[0,m],[0,n]
    vector<vector<int>> c(m + 1, vector<int>(n + 1));

    // 初始化边界,用于下面开始i=0时的c[i-1,j-1]之类的获取0值
    // 初始化列为0,空过c[0,0]
    for (int i = 1; i <= m; i++)
    {
        c[i][0] = 0;
    }
    // 初始化行为0,顺便填上c[0,0]
    for (int j = 0; j <= n; j++)
    {
        c[0][j] = 0;
    }

    // 最大子串的最后一个字符的下标
    int end_index = -1;
    // 最大子串的长度
    int length_substring = 0;

    // 先行后列,是逐行的效果
    // 每行
    for (int i = 1; i <= m; i++)
    {
        // 每列
        for (int j = 1; j <= n; j++)
        {
            // 一样字符,看左上角↖,其值+1
            // 注意,x、y从0开始
            if (x[i - 1] == y[j - 1])
            {
                c[i][j] = c[i - 1][j - 1] + 1;
            }
            // 不一样的字符,就断了,0
            else
            {
                c[i][j] = 0;
            }

            if (c[i][j] > length_substring)
            {
                length_substring = c[i][j];
                // 注意,x、y从0开始
                end_index = i - 1;
            }
        }
    }
    // 输出
    for (int i = end_index - length_substring + 1; i <= end_index; i++)
    {
        printf("%c", x[i]);
    }
}
int main(void)
{
    char x[] = "xzyzzyx";
    char y[] = "zxyyzxz";
    int m = strlen(x);
    int n = strlen(y);

    // 解题
    LCS_substring(x, y);
    return 0;
}

Reference

Longest Common Substring(最长公共子串)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值