DP 最长公共子序列并标记输出

这篇博客详细介绍了如何使用动态规划解决最长公共子序列问题,包括转移方程的建立、二维数组f[][]的初始化及状态更新,并通过标记数组path[][]记录解题路径,最后给出递归打印最长子序列的方法。
摘要由CSDN通过智能技术生成
/**
    动规是必考题目,有时甚至考好几道,非常灵活,非常巧妙

    最长公共子序列是动规的经典题目,也被用于好多教材的例子
    谈到动态规划,我对其也是最肤浅的认识,感觉和记忆化搜索
    是不分家的,我一般都是先把整个问题局部化,然后分析局部
    大致写个 转移方程 再验证是否正确并考虑特殊情况,不管怎
    么样,只要经过大量重复练习,神马都是浮云

    求其最长的子序列,如:
    abcdef
    bdaf
    这两个串的最长子序列为 bdf

    子序列和子串不一样,子串必须连续,子序列不需要连续,它
    只抢到字母的先后顺序,如果求最长子串,这就难了,当然可
    以被后缀数组秒杀,后面会贴

    只要提到两个串,然后又和DP 挂钩,大多都是二维数组f[M][M],
    M为串的最大长度,f[i][j] 表示a串前 i的字符和b 串前 j 的字
    符所得到的最优解,逐个递推到f[M][N];

    这个并不好理解,只有自己一遍遍手动模拟,然后才会有感觉,

    char s1[M], s2[M];
    scanf("%s%s", s1+1, s2+1);
    //这样输入可以使下面处理简单点,即s1[1] 为串的第一个字符,而不是s1[0]
    int f[M][M]; //用来保存子问题和问题的结果
    //f 数组初始化为0

    //设l1, l2 为s1, s2串长度
    for (int i=1; i<=l1; i++)
        for (int j=1; j<=l2; j++) {  //之所以从1 开始,因为下面有 f[i-1][j-1]
            if (s1[i] == s2[j])
                f[i][j] = f[i-1][j-1] + 1; //这是显然的
            else
                f[i][j] = max(f[i-1][j], f[i][j-1]); //如果不相等就继承一个最大的
        }
    上面虽然看起来简单,但是对于刚接触的编程的大学生理解起来也是很费劲的
    如果用手也模拟不出来,最好还是本算法书看下具体意思了,相信几乎每本上面
    都有这个例子

    上面f[l1][l2] 给出了最常的公共子序列长度,可是如果想具体打印出来怎么办
    当然得标记下

    int path[M][M]; //这是一个标记数组,总共有3 种情况,所以有3 种取值
    //初始化为0

    for (int i=1; i<=l1; i++)
        for (int j=1; j<=l2; j++) {
            if (s1[i] == s2[j]) {
                path[i][j] = 1;     //这种情况标记为1
                f[i][j] = f[i-1][j-1] + 1;
            }
            else if (f[i-1][j] > f[i][j-1]) {
                path[i][j] = 2;
                f[i][j] = f[i-1][j];
            }
            else {
                path[i][j] = 3;
                f[i][j] = f[i][j-1];
            }

        }

    //现在path 已经标记了所有的步骤,现在来递归输出

    void print(int i, int j) {
        if (path[i][j] == 0)
            return;
        if (path[i][j] == 1) {
            print(i-1, j-1);
            printf("%c ", s1[i]); //当然也可以是 s2[j]
        }
        else if (path[i][j] == 2)
            print(i-1, j);
        else
            print(i, j-1);
    }

    调用 print(l1, l2); 即可输出

    POJ 2250


    睡觉了,睡觉了,晚安
*/

收藏于 2012-01-15
来自于百度空间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值