[JD15] 括号匹配方案

JD15 括号匹配方案

一道明显是栈,但是不好下手的题目。

题目描述

合法的括号匹配序列被定义为:

  1. 空串""是合法的括号序列
  2. 如果"X"和"Y"是合法的序列,那么"XY"也是一个合法的括号序列
  3. 如果"X"是一个合法的序列,那么"(X)"也是一个合法的括号序列
  4. 每个合法的括号序列都可以由上面的规则生成

例如"", "()", "()()()", "(()())", "(((())))"都是合法的。 东东现在有一个合法的括号序列s,一次移除操作分为两步:

  1. 移除序列s中第一个左括号
  2. 移除序列s中任意一个右括号.保证操作之后s还是一个合法的括号序列

东东现在想知道使用上述的移除操作有多少种方案可以把序列s变为空
如果两个方案中有一次移除操作移除的是不同的右括号就认为是不同的方案。
例如: s = "()()()()()",输出1, 因为每次都只能选择被移除的左括号所相邻的右括号.
s = "(((())))",输出24, 第一次有4种情况, 第二次有3种情况, ... ,依次类推, 4 * 3 * 2 * 1 = 24

输入描述:
输入包括一行,一个合法的括号序列s,序列长度length(2 ≤ length ≤ 20).

输出描述:
输出一个整数,表示方案数

示例1

输入:
(((())))
输出:
24

解题思路

题目说的是,按照从左到右的顺序、合法移除括号对的方案的数量。这里是不好模拟的,因为对应每一个左括号要移除的右括号不固定、并且具有后效性,也就是说这一次移除右括号会对下一次移除右括号造成影响。
这里肯定要转换思路了,能不能转换成一个没有后效性的问题?
这个问题中,从左到右移除左括号的约束条件,究其本质是为了保证每次移除括号之后剩余的字符串仍然是一个合法的串。所以我们可以考虑,将移除顺序倒转,先移除右边的后移除左边的,不影响最终结果。更进一步,对于“XY”这种形式的串,我们分段来计算也不影响最终结果。于是我们可以把这个问题转化成一个栈来操作:

  • 每次遇到左括号就压栈,直到遇到右括号,表示需要开始这一段的括号匹配计算;
  • 遇到右括号之后,就把统计结果乘上栈的大小,并把最后的左括号从栈中弹出来 —— 这里乘上的是栈中左括号的个数,其实代表的是最后一个左括号可以合法匹配的右括号的数量;

当然,这里的栈也不是必要的,我们需要的只是栈大小,所以用一个简单的计数器代替也是可以的。

参考代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<char> stk;
    string s;
    cin >> s;
    int64_t res = 1;
    for (char c : s) {
        if (c == '(') stk.push(c);
        else if (c == ')') {
            res *= stk.size();
            stk.pop();
        } else assert(false);
    }
    cout << res << endl;
} // AC
posted @ 2021-10-09 15:34  与MPI做斗争  阅读(61)  评论(0编辑  收藏  举报