首页 > 其他分享 >求最长回文子序列长度问题

求最长回文子序列长度问题

时间:2022-10-07 18:22:14浏览次数:65  
标签:int length str 序列 最长 dp 回文

求最长回文子序列长度问题

作者:Grey

原文地址:

博客园:求最长回文子序列长度问题

CSDN:求最长回文子序列长度问题

题目描述

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。题目链接见:LeetCode 516. Longest Palindromic Subsequence

暴力解

定义递归函数

int process(int i, int j, char[] str) 

递归含义是:str 这个字符串从 i 到 j,最长回文子序列长度是多少。

主函数只需要调用

return process(0, str.length - 1, str);

即为要求的答案。

接下来看递归函数的实现

首先是 base case,显然有如下两个结论:

结论1:当i == j的时候,说明只有一个字符,最长回文子序列长度就是 1;

结论2:当i == j - 1的时候,如果str[i] == str[j],则最长回文子序列的长度就是 2, 否则就是 1;

接下来就是普遍情况:

要求i……j之间的最长回文子序列的长度,有如下三种情况

情况1,不考虑 i 位置的字符,则i……j之间的最长回文子序列的长度就是i+1……j之间的最长回文子序列长度。

情况2,不考虑 j 位置的字符,则i……j之间的最长回文子序列的长度就是i……j-1之间的最长回文子序列的长度。

情况3,当str[i] == str[j]的时候,i……j之间的最长回文子序列的长度就是i+1……j-1之间的最长回文子序列的长度加 2。

以上三种情况求最大值,就是i……j之间的最长回文子序列的长度。

暴力解法的完整代码如下

class Solution {
     public static int longestPalindromeSubseq(String s) {
        if (s == null || s.length() < 1) {
            return 0;
        }
        char[] str = s.toCharArray();
        return process(0, str.length - 1, str);
    }

    // i...j的最长回文子序列是多少
    public static int process(int i, int j, char[] str) {
        if (i == j) {
            return 1;
        }
        if (i == j - 1) {
            return str[i] == str[j] ? 2 : 1;
        }
        int p1 = process(i + 1, j, str);
        int p2 = process(i, j - 1, str);
        int p3 = (str[i] == str[j] ? 2 : 0) + process(i + 1, j - 1, str);
        return Math.max(p1, Math.max(p2, p3));
    }
}

LeetCode 上这个解法会直接超时

image

动态规划

通过暴力递归方法

    public static int process(int i, int j, char[] str) {
        ...
        int p1 = process(i + 1, j, str);
        int p2 = process(i, j - 1, str);
        ... process(i + 1, j - 1, str);
        ....
    }

我们可以得到一个结论,原问题是一个二维数组规模的问题,使用一个二维数组就可以把整个递归中的解保存下来,二维数组定义如下

int[][] dp = new int[s.length()][s.length()];

dp[i][j]就是递归函数process(i,j,str)的含义,即:str 这个字符串从 i 到 j,最长回文子序列长度是多少。

且任何一个(i,j)位置依赖三个位置的值,即:(i,j-1),(i+1,j),(i+1,j-1)

二维数组的对角线位置的值都是 1,因为对角线i == j,只有一个字符,最大回文子序列就是 1,

接下来按照递归含义依次填好每个二维数组格子的值,说明见注释

      for (int i = 0; i < s.length(); i++) {
        // 对角线都是1
            dp[i][i] = 1;
            if (i != s.length() - 1) {
                // 对角线上一条线 不是 1 就是 2 
                dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
            }
        }

        // 普遍位置
        for (int index = 2; index < s.length(); index++) {
            int i = 0;
            int j = index;
            while (j < s.length()) {
                int p1 = dp[i + 1][j];
                int p2 = dp[i][j - 1];
                int p3 = (str[i] == str[j] ? 2 : 0) + dp[i + 1][j - 1];
                dp[i][j] = Math.max(p1, Math.max(p2, p3));
                i++;
                j++;
            }
        }
        // 返回dp[0][s.length() - 1]: 即 整个字符串的最长回文子序列的长度
        return dp[0][s.length() - 1];

完整代码如下

class Solution {
    public static int longestPalindromeSubseq(String s) {
        if (s == null || s.length() < 1) {
            return 0;
        }
        char[] str = s.toCharArray();
        int[][] dp = new int[s.length()][s.length()];
        for (int i = 0; i < s.length(); i++) {
            dp[i][i] = 1;
            if (i != s.length() - 1) {
                dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
            }
        }

        for (int index = 2; index < s.length(); index++) {
            int i = 0;
            int j = index;
            while (j < s.length()) {
                int p1 = dp[i + 1][j];
                int p2 = dp[i][j - 1];
                int p3 = (str[i] == str[j] ? 2 : 0) + dp[i + 1][j - 1];
                dp[i][j] = Math.max(p1, Math.max(p2, p3));
                i++;
                j++;
            }
        }

        return dp[0][s.length() - 1];
    }
}

使用最大公共子序列来解

还有更多的思路可以解这个题目,比如:一个字符串和它的逆序串的最大公共子序列就是这个串的最长回文子序列,不赘述,直接看代码

class Solution {
    public int longestPalindromeSubseq(String s) {
        char[] str1 = s.toCharArray();
        int n = str1.length;
        char[] str2 = new char[n];
        for (char str : str1) {
            str2[--n] = str;
        }
        return longestCommonSubsequence2(str1, str2);
    }

    // 最长公共子序列
    public int longestCommonSubsequence2(char[] str1, char[] str2) {
        if ((null == str1 || str1.length == 0) || str2 == null || str2.length == 0) {
            return 0;
        }
        int m = str1.length;
        int n = str2.length;
        int[][] dp = new int[m][n];
        dp[0][0] = str1[0] == str2[0] ? 1 : 0;
        for (int i = 1; i < n; i++) {
            dp[0][i] = str1[0] == str2[i] ? 1 : dp[0][i - 1];
        }
        for (int i = 1; i < m; i++) {
            dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                if (str1[i] == str2[j]) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
                } else {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1]);
                }
            }
        }
        return dp[m - 1][n - 1];
    }
}

其中int longestCommonSubsequence2(char[] str1, char[] str2)方法就是求两个字符串的最长公共子序列的动态规划解法。

更多

算法和数据结构笔记

标签:int,length,str,序列,最长,dp,回文
From: https://www.cnblogs.com/greyzeng/p/16760224.html

相关文章

  • [答疑]业务序列图的虚线、实线和没有线
    ​​DDD领域驱动设计批评-文集-点击查看>>​​大熊2022-3-2511:42在学习您的课件。人事系统没有消息到主管,员工有,意思是不是直接找人?按照您说的箭头的意思是请求帮忙,备案......
  • ctfshow新手杯剪刀石头布(session反序列化)
    看到ini_set('session.serialize_handler','php');让我不由自主的想起了session反序列化漏洞的一道题。直接百度会有很多文章这里不多介绍。因此我们的解法就是:1.post一......
  • [答疑]把类拖到序列图中,弹出的框消失了,只能默认选Link
    长久2020-1-1217:11潘老师,EA13版本,按照这种拖拉的方式没有这个选项。from业务对象,怪怪的。从属性来看没有实例化,求指导UMLChina潘加宇你之前是不是勾选过这个长久是的,在尝......
  • ctfshow新手杯baby_pickle(python序列化与反序列化)
    题目附件代码如下:#Author:#Achilles#Time:#2022-9-20#For:#ctfshowimportbase64importpickle,pickletoolsimportuuidfromflaskimportFlask,......
  • CTFShow 反序列化
    序列化:是将变量转换为可保存或传输的字符串的过程反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用。notice:序列化只是将变量序列化。试图通过序......
  • 【数据结构和算法】LeetCode,初级算法-16验证回文串
    截止到目前我已经写了600多道算法题,其中部分已经整理成了pdf文档,目前总共有1000多页(并且还会不断的增加),大家可以免费下载下载链接:​​https://pan.baidu.com/s/1hjwK0ZeRxY......
  • ​排序子序列​​
    牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序......
  • #yyds干货盘点# 前端歌谣的刷题之路-第一百零三题-回文字符串
    前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从......
  • quicklib json序列
    quicklibjson序列quicklib面向MODEL的JSON序列。unitUnit2;///<author>cxg2022-6-14</author>interfaceusesquick.Json.Serializer,Quick.MemoryCache.Seri......
  • 最长公共子序列
    最长公共子序列给定两个长度分别为\(n,m\)的序列试求出最长的公共子序列。\(\mathcalO(n^2)\)做法我们考虑进行动态规划设\(f[i][j]\)表示看完\(a\)数组的前......