多组括号匹配:一趟从左到右扫描,栈存未配对左括号下标;遇栈空右括号标 ?,扫描后残留左括号标 $,线性时间。
OJ: roj
题目 ID: 1203
难度:入门
标签:字符串栈模拟python
创建: 2026-09-29 23:28
更新: 2026-10-05 05:27
题目描述
多组数据读到 EOF,每行只含 (、) 与大小写字母,长度不超过 ( 与它右边距离最近的 ) 配对(从内到外)。对每行输出两行:第一行原串,第二行等长标注——多余的 ( 标 $,多余的 ) 标 ?,其余位置留空格。样例见 problem.md。
思路
从左到右扫一遍,用栈存尚未配对的左括号下标:读到 ( 入栈,读到 ) 时栈非空则弹栈配对、栈空则该 ) 标 ?;扫描结束后栈里剩下的左括号全部标 $。每字符最多入/出栈一次,时间
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-29 23:28
# update_at: 2026-09-29 23:28
import sys
def marked_line(s: str) -> str:
"""返回标注行:多余的左括号标 $,多余的右括号标 ?,其余位置留空格。"""
stack: list[int] = [] # 尚未配对的左括号下标,"距离最近"就是栈顶
unmatched_right: set[int] = set()
for i, ch in enumerate(s):
if ch == '(':
stack.append(i)
elif ch == ')':
if stack:
stack.pop() # 与左边最近的未配对左括号配对
else:
unmatched_right.add(i)
unmatched_left = set(stack) # 扫完仍留在栈里的左括号永远找不到搭档
return ''.join(
'$' if i in unmatched_left else '?' if i in unmatched_right else ' '
for i in range(len(s))
)
def solve() -> None:
lines = sys.stdin.read().split() # 一行一组数据,组间空白分隔
sys.stdout.write(''.join(f'{s}\n{marked_line(s)}\n' for s in lines))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 05:27
* update_at: 2026-10-05 05:28
*/
// main.cpp:括号匹配。栈存尚未配对的左括号下标,"最近的左括号"就是栈顶。
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int MAXN = 105; // 单行长度 ≤ 100,留 5 个余量
int st[MAXN]; // 栈:尚未配对的左括号下标
int top_; // 栈顶指针(用 top_ 避免和 std 冲突)
bool badL[MAXN]; // badL[i] = 第 i 个 '(' 无法配对
bool badR[MAXN]; // badR[i] = 第 i 个 ')' 无法配对
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
// 多组输入,按行读直到 EOF
while (getline(cin, s)) {
if ((int)s.size() > MAXN) continue; // 题目限制外直接跳过
// 重新初始化当前行的标记
for (int i = 0; i <= (int)s.size(); ++i) { badL[i] = badR[i] = false; }
top_ = 0;
for (int i = 0; i < (int)s.size(); ++i) {
char ch = s[i];
if (ch == '(') {
st[++top_] = i; // 入栈:当前 '(' 成为最近的未配对左括号
} else if (ch == ')') {
if (top_ > 0) {
--top_; // 与栈顶配对
} else {
badR[i] = true; // 栈空:右括号失配
}
} // 字母不入栈也不打标记
}
// 扫描结束仍留在栈里的左括号永远找不到搭档
while (top_ > 0) { badL[st[top_--]] = true; }
// 输出原串与标注串
cout << s << '\n';
for (int i = 0; i < (int)s.size(); ++i) {
if (badL[i]) cout << '$';
else if (badR[i]) cout << '?';
else cout << ' ';
}
cout << '\n';
}
return 0;
}