动态规划实例(十二):最长回文子串(LPS)

本文介绍如何使用动态规划解决最长回文子序列(LPS)问题,包括递归和动态规划两种方法的实现,并通过示例代码展示算法的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     给一个字符串,找出它的最长的回文子序列的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。 “BBBBB”和“BBCBB”也都是该字符串的回文子序列,但不是最长的。注意和最长回文子串的区别(参考:最长回文串)!这里说的子序列,类似最长公共子序列LCS( Longest Common Subsequence)问题,可以是不连续的。这就是LPS(Longest Palindromic Subsequence)问题。
    最直接的解决方法是:生成给定字符串的所有子序列,并找出最长的回文序列,这个方法的复杂度是指数级的。下面来分析怎么用动态规划解决。
    1)最优子结构
        假设 X[0 ... n-1]  是给定的序列,长度为n.  让 L(0,n-1) 表示 序列 X[0 ... n-1] 的最长回文子序列的长度。
        1. 如果X的最后一个元素和第一个元素是相同的,这时:L(0, n-1) = L(1, n-2) + 2 ,  还以 “BBABCBCAB” 为例,第一个和最后一个相同,因此 L(1,n-2) 就表示蓝色的部分。

        2. 如果不相同:L(0, n-1) = MAX ( L(1, n-1) ,  L(0, n-2) )。 以”BABCBCA” 为例,L(1,n-1)即为去掉第一个元素的子序列,L(0, n-2)为去掉最后一个元素。

    2)重复子问题

具体实例及实现代码如下所示:

/**
 * @Title: LPS.java
 * @Package dynamicprogramming
 * @Description: TODO
 * @author peidong
 * @date 2017-6-12 上午9:36:55 
 * @version V1.0
 */
package dynamicprogramming;

/**
 * @ClassName: LPS
 * @Description: 最长回文子序列
 * @date 2017-6-12 上午9:36:55 
 *
 */
public class LPS {

    /**
     *
     * @Title: lpsRecursion
     * @Description: 利用递归求解LPS
     * @param seq 字符串
     * @param i  起始位置
     * @param j  终止位置
     * @return
     * @return int
     * @throws
     */
    public static int lpsRecursion(String seq, int i, int j){
        //边界条件,只有一个元素
        if(i == j)
            return 1;
        if(i > j)
            return 0; //只计算序列seq[i...j]
        //如果首尾相同
        if(seq.charAt(i) == seq.charAt(j))
            return lpsRecursion(seq, i+1, j-1) + 2;

        //首尾不同
        return Math.max(lpsRecursion(seq, i, j-1), lpsRecursion(seq, i+1, j));
    }

    /**
     *
     * @Title: lps
     * @Description:利用动态规划求解LPS
     * @param seq
     * @param n
     * @return
     * @return int
     * @throws
     */
    public static int lps(String seq, int n){
        //创建状态转移矩阵
        int[][] tc = new int[n][n];
        int temp;
        //初始化状态转移矩阵
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                tc[i][j] = 0;
            }
        }
        for(int i = 0; i < n; i++)
            tc[i][i] = 1;
        //构建状态转移矩阵
        for(int i = 1; i < n; i++){
            temp = 0;
            //考虑所有连续的长度为i+1 的子串
            for(int j = 0; j + i < n; j++){
                //如果首尾相同
                if(seq.charAt(j) == seq.charAt(j+i))
                    temp = tc[j+1][j+i-1]+2;
                else
                    temp = Math.max(tc[j+1][j+i], tc[j][j+i-1]);
                tc[j][j+i] = temp;
            }
        }
        return tc[0][n-1];
    }
    /**
     * @Title: main
     * @Description: TODO
     * @param args
     * @return void
     * @throws
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String seq = "acmerandacm";
        int n = seq.length();
        System.out.println("利用递归求解LPS结果:"+ lpsRecursion(seq, 0, n-1));
        System.out.println("利用dp求解LPS结果:"+ lps(seq, n));
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值