Brackets sequence UVA - 1626 括号序列 线性dp

本文介绍了一种算法,用于在给定的括号序列中添加最少数量的括号,使其成为一个正规括号序列。该算法通过动态规划实现,考虑了括号配对的可能性,并提供了打印解决方案的方法。

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

题目链接

定义如下正规括号序列(字符串):

  1. 空序列是正规括号序列。
  2. 如果S是正规括号序列,那么(S)和[S]也是正规括号序列。
  3. 如果A和B都是正规括号序列,那么AB也是正规括号序列。

例如,下面的字符串都是正规括号序列:(),[],(()),([]),()[],()[()],而如下字符串则不是正规括号序列:(,[,],)(,([()。输入一个长度不超过100的,由“(”、“)”、“[”、“]”构成的序列,添加尽量少的括号,得到一个规则序列。如有多解,输出任意一个序列即可。
【分析】
设串S至少需要增加d(S)个括号,转移如下:

  1. 如果S形如(S′)或者[S′],转移到d(S′)。
  2. 如果S至少有两个字符,则可以分成AB,转移到d(A)+d(B)。

边界是:S为空时d(S)=0,S为单字符时d(S)=1。注意(S′, [S′, ) S′之类全部属于第二种转移,不需要单独处理。注意:不管S是否满足第一条,都要尝试第二种转移,否则“[][]”会转移到“][”,然后就只能加两个括号了。当然,上述“方程”只是概念上的,落实到程序时要改成子串在原串中的起始点下标,即用d(i,j)表示子串S[i~j]至少需要添加几个括号。本题需要打印解,如何打印解呢?可以在打印时重新检查一下哪个决策最好。这样做的好处是节约空间,坏处是打印时代码较复杂,速度稍慢,但是基本上可以忽略不计(因为只有少数状态需要打印)。

本题唯一的陷阱是:输入串可能是空串,因此不能用scanf("%s", s)的方式输入,只能用getchar、fgets或者getline。

// 算法:形如(S)或者[S],转移到d(S),然后分成AB,转移到d(A)+d(B)。注意(S, [S, )S之类全部属于第二种转移。
// 注意输入有空行。
// 本程序是递推解法
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 100 + 5;
char S[maxn];
int n, d[maxn][maxn];

bool match(char a, char b) {
  return (a == '(' && b == ')') || (a == '[' && b == ']');
}

void dp() {
  for(int i = 0; i < n; i++) {
    d[i+1][i] = 0;
    d[i][i] = 1;
  }
  for(int i = n-2; i >= 0; i--)
    for(int j = i+1; j < n; j++) {//每次s[i]和后面的s[j]匹配更新
      d[i][j] = n;
      if(match(S[i], S[j])) d[i][j] = min(d[i][j], d[i+1][j-1]);
      for(int k = i; k < j; k++)
        d[i][j] = min(d[i][j], d[i][k] + d[k+1][j]);
    }
}

void print(int i, int j) {
  if(i > j) return ;
  if(i == j) {
    if(S[i] == '(' || S[i] == ')') printf("()");
    else printf("[]");
    return;
  }
  int ans = d[i][j];
  if(match(S[i], S[j]) && ans == d[i+1][j-1]) {
    printf("%c", S[i]); print(i+1, j-1); printf("%c", S[j]);
    return;
  }
  for(int k = i; k < j; k++)
    if(ans == d[i][k] + d[k+1][j]) {
      print(i, k); print(k+1, j);
      return;
    }
}

void readline(char* S) {
  fgets(S, maxn, stdin);
}

int main() {
  int T;

  readline(S);
  sscanf(S, "%d", &T);
  readline(S);

  while(T--) {
    readline(S);
    n = strlen(S) - 1;
    memset(d, -1, sizeof(d));
    dp();
    print(0, n-1);
    printf("\n");
    if(T) printf("\n");
    readline(S);
  }
  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值