【算法青年】最长回文子序列

刚刚考完操作系统,虽然操作系统考试已经跪了,但是算法OJ还得继续写(我哭爆),好在这次算法OJ(作业8)不难,是两道简单的动态规划问题,下面是问题A


问题 A: 最长回文子序列

时间限制: 1 Sec  内存限制: 75 MB

题目描述

回文:一个序列是回文的,是指这个序列从左往右读,从右往左读是一样的。

子序列:一个序列a1, a2, …, an,它的子序列指的是从中任选几个元素,按照其原先序号从小到大排好。比如a5, a9, a11就是原序列的一个子序列(假设n>=11)。

求:一个序列的最长回文子序列的长度。

输入

一个字符序列,字符之间以空格隔开

输出

最长回文子序列的长度

样例输入

A C G T G T C A A A A T C G 

样例输出

8

提示

算法的时间复杂度不要超过O(n2)
使用short而不是int以节省内存
使用scanf而不是cin来节约时间和内存
少做没有意义的操作,否则可能会时间超限


以上就是问题A,求最长回文子序列。首先什么是回文子序列呢,就是正着看和倒着看都一样的序列。注意这里是子序列,而不是字串,如果是字串,那么还隐含了另一个条件,那就是连续。

这道题直观上看是需要使用递归的,对于给定长度,设为n,的字符序列A[1...n],A[1...n]中最长子序列的长度分为以下两种情况:

1、A[1] == A[n]

    这种情况下,A[1...n]的最长回文子序列长度是A[2...n-1]最长回文子序列的长度加2。可以证明A[2...n-1]的最长回文子序列的长度不短于A[1...n-1]和A[2...n],而又有A[1]==A[n],所以A[1], A[n]加上A[2...n]中最长回文子序列的长度是要大于A[1...n-1]和A[2...n]的,这就证明了算法的正确性。

2、A[1] != A[n]

    这种情况下,1中那种情况已经是不可能了,因此只有从剩下的两种情况——A[1...n-1]和A[2...n]的最长回文子序列长度中选取那个最大的了。

递归伪代码如下(请注意!伪代码加*注释的行没有考虑边界条件)

MAX_SEQ(A, begin, end){

    if A[begin] == A[end]

        return 2 + MAX_SEQ(A, begin + 1, end - 1);    //*

    else

        return max{MAX_SEQ(A, begin, end - 1), MAX_SEQ(A, begin + 1, end)};    //*

}

众所周知的是,递归效率极其之低,以至于尽管是一道规模不大OJ题,OJ服务器也不想拿出很多时间和资源去进行递归计算(开玩笑的)。动态规划的出现就是为了解决递归中存在的巨量冗余计算,其思想就是把已经执行过一遍的计算记录下来,下次再执行这个计算时,直接将上一次的计算值取出来使用,避免了二次计算。

动态规划相关知识参见《算法设计与分析》(黄宇 著    机械工业出版社)第14章.

这道题是从字符序列的两端向内求解,需要记录从i开始到j这段字符序列中最大回文序列,i和j都是可变动值,因此是一个二维的动态规划问题。以下给出动态规划数组递归式:

MAX_SEQ[i, j] =      2 + MAX_SEQ[i + 1, j - 1], seq[i] == seq[j] && i + 1 < j

                                2,    i + 1 == j

                                max{MAX_SEQ[I, j - 1], MAX_SEQ[i + 1, j] }, seq[i] != seq[j]

最终输出MAX_SEQ[0, n-1]即可。



实现中的一些问题:


1、动态二维数组

动态数组使用起来很方便,但是动态数组只能有一维是动态的,所以不可能进行如下申请:

int* MAX_SEQ = new int[seq.size][seq.size];

而神器vector又只是一维的。那不想额外浪费空间(注意到本题内存限制高达75MB,而我只用了8MB。。。所以节省空间的考虑可能是多此一举的),该怎么办呢,是不是有想自己封装动态数组的想法?回忆前几次OJ中,用vector储存图结构时,每个点的结构体内部又封装了一个vector,便自然而然产生了以下办法,在vector中嵌套一个vector,具体实现如下:

vector<vector<int>>MAX_SEQ(seq.size);

这样就有了一个二维动态数组,但是每一行现在都是空的,此时利用vector提供的resize函数(注意!不是reserve函数!)对列进行扩充,如下:

for(int i = 0; i < seq.size; i++)

    MAX_SEQ[i].resize(seq.size);

这样,就成功申请了一个size*size的二维数组,满足了按需取内存的要求


2、初始化和计算顺序问题

首先要把MAX_SEQ[i][i]初始化为1,因为任何长度为1的序列都是回文序列。

计算动态规划数组时,显然要贴着矩阵(二维数组)的对角线计算,从程序的空间局部性和for循环的易读性考虑,建议从倒数第二行往前,每一行从对角线往右遍历

for i = seq.size - 2 to  0 do

    for j = i + 1 to seq.size - 1 do

        /*do what you want to do*/


鸣谢

Aaron leo

### 最长回文子序列问题中的字典序算法 对于最长回文子序列问题,通常的目标是最小化或最大化其字典序。这意味着,在找到具有最大长度的回文子序列之后,还需要进一步优化这些子序列的排列方式以满足特定条件。 #### 动态规划解决最长回文子序列 动态规划是一种常见的解决方案来计算最长回文子序列的长度[^2]。然而,当涉及到字典序时,我们需要扩展基本的动态规划方法,以便不仅记录长度还记录具体的子序列及其字典顺序。 以下是基于动态规划的方法并结合字典序处理的一个实现: ```python def longest_palindromic_subsequence(s): n = len(s) dp = [[[] for _ in range(n)] for __ in range(n)] # 初始化单字符的情况 for i in range(n): dp[i][i].append(s[i]) # 构建dp表 for length in range(2, n + 1): # 子串长度从2到n for start in range(n - length + 1): end = start + length - 1 if s[start] == s[end]: temp = [s[start] + subseq + s[end] for subseq in dp[start + 1][end - 1]] dp[start][end] = min(temp, key=lambda x: (len(x), x)) # 使用最小字典序 else: left_sequences = dp[start][end - 1] right_sequences = dp[start + 1][end] merged = list(set(left_sequences + right_sequences)) dp[start][end] = sorted(merged, key=lambda x: (-len(x), x))[0:] # 排序按长度降序和字典升序 result_list = dp[0][n - 1] final_result = min(result_list, key=lambda x: (len(x), x)) # 获取最终结果 return final_result # 测试用例 print(longest_palindromic_subsequence("bbbab")) # 输出:"bbbb" print(longest_palindromic_subsequence("cbbd")) # 输出:"bb" ``` 上述代码通过构建二维数组 `dp` 来存储每一对索引之间的所有可能的回文子序列,并利用 Python 的内置函数对它们按照长度优先、其次字典序的方式排序[^3]。 #### 时间复杂度分析 此方法的时间复杂度主要由两部分组成:一是填充 DP 表的过程 \( O(n^2) \),二是每次更新过程中涉及的列表操作(如去重、排序),这可能会增加额外开销至 \( O(k\log k) \),其中 \( k \) 是当前状态下的候选子序列数量。因此整体时间复杂度接近于 \( O(n^3) \)[^4]。 #### 空间复杂度分析 由于需要保存所有的中间状态以及对应的子序列集合,空间需求较高,大约为 \( O(n^2) \). --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值