news 2026/6/25 18:37:41

【区间DP】括号序列:如何求解最长合法子序列?(POJ 2955)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【区间DP】括号序列:如何求解最长合法子序列?(POJ 2955)

在区间动态规划的题库中,“括号匹配”类问题占据了半壁江山。

很多同学分不清“最长合法子串”和“最长合法子序列”的区别:

  • 子串 (Substring):必须连续。

  • 子序列 (Subsequence):可以不连续,中间可以跳过某些字符。

今天我们要解决的,就是一道经典的“子序列”问题。给定一个由()[]组成的字符串,求出其中最长的合法括号子序列的长度。


1. 问题背景

题目描述

给定一个长度为n () 的字符串S。我们要在这个字符串中挑选出一些字符,保持它们在原串中的相对顺序不变,组成一个新的字符串。

要求这个新字符串必须是“合法的括号序列”。

合法定义的递归形式如下:

  1. 空串是合法的。

  2. 如果A是合法的,那么(A)[A]也是合法的。

  3. 如果A和B是合法的,那么AB也是合法的。

目标:求这个最长合法子序列的长度。

样例

输入:(( ))输出:4

输入:([)]输出:2 (可以是()或者[],但不能交叉)


2. 算法分析

这道题是典型的区间DP模型中的“端点匹配型”。

状态定义

我们定义dp[i][j]表示:字符串S中,从下标i到j的区间内,最长合法子序列的长度

状态转移

对于区间[i, j],我们依然采用“最后一步”的思维来推导。想要得到[i, j]的最优解,有两种大的构成方式:

策略一:两端匹配 (包围结构)

如果区间两端的字符S[i]和S[j]恰好能配对(即()[]),那么它们有资格成为一对新的括号,包裹住中间的子序列。

dp[i][j] =

(注:前提是S[i]和S[j]必须匹配)

策略二:区间拼接 (并列结构)

无论两端是否匹配,最优解都有可能由两个独立的合法子序列拼凑而成。

我们枚举分割点k(),将大区间切分为[i, k]和[k+1, j]两部分:

dp[i][j] =

提示:这就解释了为什么代码中会有两层逻辑。第一层if处理“包围”,第二层for k处理“拼接”。这两种策略覆盖了所有合法括号序列的生成规则。


3. 完整代码

//括号序列 #include <iostream> #include <cstring>//对应memset using namespace std; char s[510]; int dp[510][510];//dp[i][j]代表s[i]-s[j]之间最长的合法子序列长度 int main(){ int n; cin>>n; memset(dp,0,sizeof(dp));//初始化dp为0 //读入字符串,下标从1开始 scanf("%s",s+1); //枚举区间长度 len代表区间长度 从小到大计算出所有所有区间的合法子序列长度,计算大区间必须先计算出小的 for(int len=2;len<=n;len++){ for(int i=1;i<=n-len+1;i++){//枚举:左端点i (确保 i+len-1不越界) int j=i+len-1;//右端点 //1尝试“包围结构”,判断s[i]和s[j]是否配对 if((s[i]=='('&&s[j]==')') ||(s[i]=='['&&s[j]==']')) //如果配对,长度=中间部分的长度 + 2 //这里的dp[i+1][j-1]已经在len-2的时候算过了 dp[i][j]=max(dp[i][j],dp[i+1][j-1]+2); //2尝试“拼接结构”,枚举分割点k,取最大值,这个循环同时也覆盖了“丢弃 s[i]”或“丢弃 s[j]”的情况 for(int k=i;k<j;k++){//分界线k dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]); } } } cout<<dp[1][n]; return 0; }

4. 易错点与总结

  1. 子序列 vs 子串

    这道题求的是子序列,所以我们可以利用dp[i][k] + dp[k+1][j]来进行拼接。如果是求子串(必须连续),则不能简单相加,需要更复杂的逻辑判断(通常涉及栈或更严格的 DP 状态)。

  2. 为什么不需要专门处理“不选s[i]”的情况?

    有同学会问,为什么方程里没有写dp[i][j]=dp[i+1][j]

    其实,代码中的for(int k=i;k<j;k++)已经包含了这种情况。

    • 当k=i时,dp[i][i] + dp[i+1][j],因为dp[i][i](单个字符)必然为0,所以这就等价于dp[i+1][j]

    • 这个枚举k的循环很强大,它隐式地包含扔掉头和扔掉尾的决策。

  3. 循环顺序

    一定记得先枚举长度len,因为计算大区间dp[i][j]时,必须保证其内部的小区间(如dp[i+1][j-1])已经被计算过了。如果按i, j顺序枚举,可能会用到未更新的状态。

掌握了这道题,你就掌握了“括号区间 DP”的解题模板。以后遇到“括号匹配求最小添加数”之类的变种题,只需要把max改成min,逻辑是一样的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/21 17:32:08

Bash学习笔记总目录

shell编程是 Linux / 服务器运维、开发的基础技能&#xff0c;处理系统级任务更简洁、无环境依赖。将重复的操作自动化&#xff0c;大幅降低手动操作成本。 Bash是日常工作中用得较多的工具&#xff0c;之前也看过基本shell编程的书&#xff0c;或零星的查看帮助和文档。但一直…

作者头像 李华
网站建设 2026/6/20 21:06:20

墨蝌签名平台——可视化操作IPA重签名工具

作为一名经常使用IPA签名的使用者来说&#xff0c;今天给大家推荐墨蝌签名平台。墨蝌签名平台的优势墨蝌签名平台提供稳定高效的IPA签名服务&#xff0c;支持多种证书类型&#xff0c;包括企业证书和个人开发者证书&#xff0c;满足不同用户的需求。丰富的功能特性平台具备自动…

作者头像 李华
网站建设 2026/6/25 18:03:25

论文AI率99%?这几款降低ai率工具亲测好用,拒绝论文变“草稿”!

说实话&#xff0c;眼看着论文初稿截止日期就在眼前&#xff0c;结果一查论文ai率直接飙到99%&#xff1f;那一刻真的是脑袋“嗡”的一声。辛辛苦苦肝出来的几万字&#xff0c;被判定成“AI生成”&#xff0c;这种崩溃的心情我太懂了。其实呢&#xff0c;我也经历过那种绝望&am…

作者头像 李华
网站建设 2026/6/25 18:11:05

《Foundation 图标》

《Foundation 图标》 引言 在当今数字化时代,图标已成为信息传达的重要媒介。它们简洁明了,能够迅速传达信息,提升用户体验。本文将深入探讨Foundation图标的设计理念、应用场景及其在界面设计中的重要性。 一、Foundation图标的起源与发展 1.1 起源 Foundation图标起源…

作者头像 李华
网站建设 2026/6/23 17:59:08

wpf之行为

前言 行为是WPF中用于增强UI元素功能的一种重要模式&#xff0c;它允许在不修改原始控件代码的情况下&#xff0c;为控件添加交互逻辑。它可以封装某些功能&#xff08;如拖放、命令执行、状态管理等&#xff09;&#xff0c;使这些功能可以在不同控件间复用 1、新建行为类 …

作者头像 李华
网站建设 2026/6/24 5:58:37

React Native for OpenHarmony:简易计算器应用的开发与跨平台适配实践

简易计算器应用的开发与跨平台适配实践 摘要1. 引言&#xff1a;为何选择计算器作为 OpenHarmony RN 入门项目&#xff1f;2. 技术栈与开发环境2.1 核心依赖版本 3. 核心状态管理设计3.1 状态流转逻辑3.2 使用 useCallback 优化性能 4. 核心计算逻辑实现4.1 基础计算函数4.2 等…

作者头像 李华