首页 > 其他分享 >DP系列-最长回文子序列

DP系列-最长回文子序列

时间:2023-01-02 16:35:04浏览次数:48  
标签:return self cache DP 序列 dp 回文

原题意:
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
回文序列:如果一个序列逆置之后跟原序列是一样的就称这样的序列为回文序列,如aba、123321等。
示例1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

常规推导
定义字符串s长度为m,定义函数f(i, j)为字符串s在[i,j]区间的最长回文子序列,有三种选择:

即:
1、当s的长度为1时,则s本身就是回文串,并且最长长度为1。
2、当s的i位置上的字符等于s的j位置上的字符时,长度加1,然后再递归去比较s的i+1和s的j-1位置字符。
3、当s[i]不等于s[j]时,分两种情况,第一种s[i+1]和s[j]位置比较,第二种s[i]和s[j-1]位置比较,因为要取最长长度,所以取两种情况的最大值。
定义字符串s="bbbab",则:
当i=0,j=4时s[0] == s[4],然后去递归i+1和j-1的位置,并且此时的长度为2。

当i=1,j=3时s[1] != s[3],此时需要比较i、j-1位置和i+1、j位置,即:(s[1]、s[2]和s[2]、s[3])然后取两者最大值。

可以看出i=1,j=2时,s[1]==s[2],当i=2,j=3时,s[2]!=s[3],所以继续递归i=1,j=2并且长度加2。

当i=2,j=1时,已经超出区间[i,j]。

根据推导过程,尝试着写出代码:

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        return self.process(s, 0, len(s) - 1)

    def process(self, s, i, j):
        """
        s在区间[i,j]上的最长回文子序列
        """
        # 超出区间直接退出
        if i < 0 or j > len(s) or i > j:
            return 0
        # 只有一个字符时,长度为1
        if i == j:
            return 1
            
        if s[i] == s[j]:
            return self.process(s, i + 1, j - 1) + 2
        return max(self.process(s, i + 1, j), self.process(s, i, j - 1))

分析下,时间复杂度,空间复杂度为O(m)。这样数据量大的时候,非常耗时的,因为有很多重复的运算,解决办法就是加缓存,让重复的计算只进行一次。
记忆化搜索
分析下process函数中只有i和j的变化会得到不同的结果,所以定义二维数组cache,初始化为None,长宽分别为s的长度m,因为字符串索引从0开始,所以字符串的长度可以包含所有字符串。

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        m = len(s)
        cache = [[None] * (m + 1) for _ in range(m + 1)]
        return self.process(s, 0, len(s) - 1, cache)

    def process(self, s, m, n, cache):
        """
        带缓存的,符串在m~n上的最长回文子序列
        """
        if m < 0 or n > len(s) or m > n:
            return 0
        if m == n:
            return 1

        if cache[m][n]:
            return cache[m][n]
        if s[m] == s[n]:
            result = self.process1(s, m + 1, n - 1, cache) + 2
        else:
            result = max(self.process1(s, m + 1, n, cache), self.process1(s, m, n - 1, cache))
        return result

动态规划
根据递归试着画出动态规划的转移图,我们需要设置一个二维数组dp,dp[i][j]表示字符串s在区间[i,j]上的最长回文子序列的长度,所以我们求得dp[0][m]就代表最终的答案,并且dp[i][j]依赖i+1、j-1的位置,每个格子都依赖下一行和上一列,所以填表的时候,从下往上,从左往右开始。
转换图如下:

当i==j(对角线的位置)时表示单个字符,并且单个字符的最长回文子串长度就是1,
状态转移为

if s[i] == s[j]:
    dp[i][j] = dp[i + 1][j - 1] + 2
else:
    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

根据填表过程写出代码:

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        """
        动态规划
        :param s:
        :return:
        """
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        # 因为每格都依赖下一行的值和上一列的值,所以从下往上,从左往右填值
        for i in range(n - 1, -1, -1):
            # 每个字符串自己就是长度为1的回文序列
            dp[i][i] = 1
            # 只需要对角线右边的值,所以从i+1开始
            for j in range(i + 1, n):
                if s[i] == s[j]:
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

        return dp[0][n - 1]

其中时间复杂度为,空间复杂度为

标签:return,self,cache,DP,序列,dp,回文
From: https://www.cnblogs.com/imlifelong/p/17020040.html

相关文章

  • 220. 最长公共子序列问题(挑战程序设计竞赛)
    地址https://www.papamelon.com/problem/220给定两个字符串s_1s_2...s_n和t_1t_2...t_n.求出这两个字符串的最长公共子序列的长度。输入输入第一行有两个整数m和n,分......
  • 【LeeCode】9. 回文数
    【题目描述】回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,​​121​​​ 是回文,而 ​​123​​ 不是。​​​​https://leetcode.cn/problems/palindrom......
  • Django-drf-序列化器高级用法之SerializerMethodField
    在Drf框架中的serializers.py序列化中,SerializerMethodField字段是一个只读字段。它通过调用附加到的序列化程序类上的方法来获取其值。它可用于将任何类型的数据添加到对......
  • UDP套接字
     Datagram(数据报)是一种尽力而为的传送数据的方式,它只是把数据的目的地记录在数据包中,然后就直接放在网络上,系统不保证数据是否能安全送到,或者什么时候可以送到,也就是说它并......
  • 第十二章《文件与I/O流》第4节:对象序列化
    ​对象序列化和反序列化是Java程序中经常涉及的操作,Java语言提供了专门用于序列化对象的ObjectOutputStream类和用于反序列化的ObjectInputStream类,这使得Java语言完成序列......
  • 计数类dp
    例题:整数划分一个正整数\(n\)可以表示成若干个正整数之和,形如:\(n=n_1+n_2+…+n_k\),其中\(n_1≥n_2≥…≥n_k,k≥1\)。我们将这样的一种表示称为正整数\(n\)的一种......
  • 区间dp
    例题:石子合并设有\(N\)堆石子排成一排,其编号为\(1,2,3,…,N\)。每堆石子有一定的质量,可以用一个整数来描述,现在要将这\(N\)堆石子合并成为一堆。每次只能合并相邻的两......
  • Android笔记--数据存储之SharedPreferences
    SharedPreferences--轻量级存储工具(共享参数)其采用的存储结构是Key-Value的键值对方式SharedPreferences用法以及相关的简单案例记住密码的实现实现啦!那,就白天......
  • m基于simulink的16QAM和2DPSK通信链路仿真,并通过matlab调用simulink模型得到误码率曲
    1.算法概述2DPSK又称为相对相移键控,它不是利用载波相位的绝对数值传送数字信息,而是用前后码元的相对载波相位值传送数字信息。所谓相对载波相位是指本码与前一码元初相之差......
  • m基于simulink的16QAM和2DPSK通信链路仿真,并通过matlab调用simulink模型得到误码率曲
    1.算法概述      2DPSK又称为相对相移键控,它不是利用载波相位的绝对数值传送数字信息,而是用前后码元的相对载波相位值传送数字信息。所谓相对载波相位是指本码与前......