动态规划入门之LCS

求最长公共子序列,可以用典型的动态规划解法,上一篇求字符串编辑距离的dp解法也可以说是由这个案例变形而来。

 

状态变量:

 dp[i][j]表示字符串a[i]和字符串b[j]的最长公共子序列

 

状态转移方程:

if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;

else dp[i][j]=max{dp[i-1][j],dp[i][j-1]};

 

回溯路径:

得到dp表格后,可直接得到a[0..m]与b[0..n]的最长公共子序列dp[m+1][n+1];

若想得到具体的字符序列,则可以根据表格采用回溯的方法得到,也可在状态转移的过程中求出每种状态的的双亲节点,最后通过堆栈就可以把序列正序输出。

 

import java.util.Scanner;
import java.util.Stack;

/**
 *
 * @author Administrator 动态规划求两个序列的最长公共子序列
 */
public class LCS {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int num = scan.nextInt();
        while (num-- != 0) {
            char[] a = scan.next().toCharArray();
            char[] b = scan.next().toCharArray();
            int[][] dp = new int[a.length + 1][b.length + 1];          //dp[i][j]代表a[0..i-1]与b[0..j-1]最长公共子序列
            int[][] path = new int[a.length + 1][b.length + 1];
            //边界处理
            for (int i = 0; i <= a.length; i++) {
                dp[i][0] = 0;
            }
            for (int j = 0; j <= b.length; j++) {
                dp[0][j] = 0;
            }
            //状态转移方程
            for (int i = 1; i <= a.length; i++) {
                for (int j = 1; j <= b.length; j++) {
                    if (a[i - 1] == b[j - 1]) {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                        path[i][j] = 1;                              //记录双亲节点
                    } else {
                        dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                        path[i][j] = dp[i][j - 1] > dp[i - 1][j] ? 2 : 3;              //记录双亲节点

                    }
                }
            }
            System.out.println(dp[a.length][b.length]);
            //根据dp表格通过回溯找到最优路径
            Stack<Character> stack = new Stack();
            int x = a.length, y = b.length;
            while (x != 0 && y != 0) {
                if (a[x - 1] == b[y - 1]) {
                    stack.push(a[x - 1]);
                    x--;
                    y--;
                } else if (dp[x - 1][y] >= dp[x][y - 1]) {
                    x--;
                } else {
                    y--;
                }
            }
            while (!stack.empty()) {
                System.out.print(stack.pop());
            }
            System.out.println();
//            print(a.length,b.length,path,a);
//            System.out.println();
        }
    }

    public static void print(int x, int y, int[][] path, char[] a) {
        if(x==0||y==0){
            return;
        }
        if (path[x][y] == 1) {           //从左上角转移过来
            print(x - 1, y - 1, path, a);
            System.out.print(a[x-1]);
        } else if(path[x][y]==2){
            print(x,y-1,path,a);
        }else{
            print(x-1,y,path,a);
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值