题解:洛谷 P3015 Best Parenthesis
【题目来源】
洛谷:P3015 [USACO11FEB] Best Parenthesis S
【题目描述】
给定一个只包含左右括号的字符串,得分规则如下:
如果一对括号内没有括号,那么这对括号的得分为1;如果两对括号互不包含(即并列存在),那这两对括号的得分相加;如果括号内包含一对括号,那么这个括号的得分记为内部括号序列的得分 \(\times 2\)。
例如:对于这样一个字符串:() (),两对括号并列存在,则得分为 \(1+1=2\);
而对于这样一个字符串:(()),最外层的括号内层包含一对括号,则得分为 \(2 \times 1 = 2\)。
Bessie 想击败所有同事的牛,所以她需要计算某个字符串的评分。给定一个长度为 \(n\) 、只包含括号的字符串(\(2 \le N \le 100000\)),计算其得分帮助 Bessie。
【输入】
第一行,输入一个整数 \(n\)。
接下来 \(n\) 行,每行一个数字,如果是 \(0\),表示这个字符是 (;如果是 \(1\),表示这个字符是 )。
【输出】
字符串的分数,由于数字可能会变得很大,所以对 \(12345678910\) 取模。
【输入样例】
6
0
0
1
1
0
1
【输出样例】
3
【核心思想】
-
问题分析:给定一个长度为 \(N\)(\(N \le 10^5\))的括号序列,其中字符
0代表左括号(,字符1代表右括号)。根据规则计算得分:空括号()得 \(1\) 分;并列括号得分相加;嵌套括号的得分为内部得分 \(\times 2\)。需要对结果取模 \(12345678910\)。本质是括号匹配与树形结构计算问题,可以用栈模拟括号的嵌套和并列关系。 -
算法选择:
- 栈模拟:维护一个栈,每个元素存储当前层的累计得分和是否包含过括号的标记。遇到左括号时压入新层(得分 0,标记 false);遇到右括号时弹出栈顶,计算当前括号对的得分:
- 若该层内部未包含括号(标记为 false),则当前括号对为空,得分为 \(1\)。
- 若内部包含括号,则得分为
内部得分 * 2。
- 然后将当前括号对的得分加到新的栈顶元素的累计得分中(表示并列相加),并标记新栈顶为已包含括号。
- 最后栈底元素即为总得分。
- 栈模拟:维护一个栈,每个元素存储当前层的累计得分和是否包含过括号的标记。遇到左括号时压入新层(得分 0,标记 false);遇到右括号时弹出栈顶,计算当前括号对的得分:
-
关键步骤:
- 读入与转换:读取 \(N\),逐个读入数字(0 或 1)。
- 栈初始化:
stk.push({0, false}),作为总得分的容器。 - 处理左括号(0):压入新层
{0, false}。 - 处理右括号(1):
- 弹出栈顶
cur。 - 计算当前层得分:
score = cur.second ? (2 * cur.first) % mod : 1。 - 将
score加到当前栈顶的累计得分上:stk.top().first = (stk.top().first + score) % mod。 - 标记当前栈顶为包含括号:
stk.top().second = true。
- 弹出栈顶
- 输出:栈底元素的
first即为答案。
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),每个字符入栈或出栈一次。
- 空间复杂度:\(O(N)\),栈在最坏情况下存储 \(N\) 个元素。
-
栈模拟括号树的核心思想:
- 括号层次结构:括号序列天然形成一棵树,每对括号是一个节点,并列关系对应兄弟节点,嵌套关系对应父子节点。
- 栈的层叠性:用栈的压入和弹出模拟树的深度优先遍历,每个栈帧存储当前节点的累计得分和是否包含子节点。
- 得分规则转换:空括号
()对应叶子节点,得分为 1;嵌套括号对应父节点,得分为子节点得分 * 2;并列括号对应兄弟节点,得分相加。 - 取模处理:由于得分可能极大,每次加法后立即对 \(12345678910\) 取模。
- 适用场景:适用于括号序列的解析和计算,特别是涉及嵌套和并列复杂度的表达式求值。
【算法标签】
普及 #栈
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 12345678910; // 取模数
int n;
stack<pair<int, bool>> stk; // 栈元素:first为当前段累计得分,second表示该段内是否已经包含括号
signed main()
{
cin >> n;
// 栈底放入一个初始元素,作为最终总得分的容器
stk.push({0, false});
// 逐个读入字符:0 代表 '(',1 代表 ')'
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
// 左括号:压入新的一层,初始得分0,且尚未包含内部括号
if (x == 0)
{
stk.push({0, false});
}
// 右括号:处理当前最内层括号
else
{
auto cur = stk.top(); // 当前层(被右括号闭合的括号对)
stk.pop();
// 如果该层内部已经包含括号(即 cur.second == true),
// 则当前括号对的得分为内部得分乘以2;
// 否则该括号对为空,得分为1
int score = cur.second ? (2 * cur.first) : 1;
// 将当前括号对的得分加入到外层(新栈顶)的累计得分中,
// 因为并列括号得分相加
stk.top().first = (stk.top().first + score) % mod;
// 标记外层已经包含了至少一个括号(即内部不为空)
stk.top().second = true;
}
}
// 栈底元素存储了整个字符串的总得分
cout << stk.top().first << endl;
return 0;
}
【运行结果】
6
0
0
1
1
0
1
3
浙公网安备 33010602011771号