26572: 多余的括号
http://cs101.openjudge.cn/practice/26572/
小明总是记不清四则运算的优先级关系,为了保险起见,他总是在算式中加上许多冗余的括号,但层层嵌套的括号可苦了批改作业的老师。现在想请你编写一个程序,在不改变算式运算顺序的前提下,删除其中多余的括号。
为了简单起见,我们只考虑加法和乘法两种运算,其中乘法优先级高于加法。题目保证给出的算式是合法的,且所有出现的运算数都是非负整数,不含正负号。
输入是若干行只含有非负整数、加号、乘号和括号的四则运算表达式。对于每一行输入,你的程序需要输出一行结果,即删去表达式中所有冗余括号后得到的简化表达式。
输入
(1+11) ((1+2))+3*(4+5)
输出
1+11 1+2+3*(4+5)
样例输入
(1+11)
((1+2))+3*(4+5)
1+(2+3)样例输出
1+11
1+2+3*(4+5)
1+(2+3)来源: lxp
2025/6/4 修正过测试数据。正确方法应该是中缀转后缀,后缀再转中缀表达式(同时满足题面要求:不改变算式运算顺序)。
基于 中缀表达式转后缀表达式(也叫逆波兰表示法,Reverse Polish Notation,RPN)的思想,并在后缀表达式的基础上再转回中缀表达式,同时去除冗余的括号。下面代码也可以 AC。
python
import re
def infix_to_postfix(tokens):
"""
中缀(tokens)→ 后缀:
- 使用栈存运算符,根据优先级(+:1,*:2)和左结合性来出栈与入栈。
- 数字直接输出到 postfixList,遇到 '(' 入栈,遇到 ')' 则依次弹出运算符直到遇到 '('。
"""
op_stack = []
postfix = []
prec = {"+": 1, "*": 2}
for tk in tokens:
if tk.isdigit():
# 数字(包括多位)直接放到后缀列表
postfix.append(tk)
elif tk == "(":
op_stack.append(tk)
elif tk == ")":
# 右括号时,把栈顶运算符都弹出直到遇到左括号
while op_stack and op_stack[-1] != "(":
postfix.append(op_stack.pop())
op_stack.pop() # 弹出 '('
else:
# 运算符:比较优先级,优先级低(或相等)的运算符先出栈
while (op_stack and op_stack[-1] != "("
and prec[tk] <= prec[op_stack[-1]]):
postfix.append(op_stack.pop())
op_stack.append(tk)
# 最后把栈里剩余的运算符都弹出
while op_stack:
postfix.append(op_stack.pop())
return postfix
def postfix_to_infix(postfix):
"""
后缀 → 中缀(去冗余括号):
- 每次遇到数字就把 (字符串, “自身优先级”) 入栈。这里设计:
数字的“自身优先级”设为 3,
'+' 的优先级设为 1,
'*' 的优先级设为 2。
- 每次遇到 '+' 或 '*':
从栈顶弹出右操作数 (右子表达式, right_prec),
再弹出左操作数 (左子表达式, left_prec)。
设当前运算符 op 的优先级为 prec_op('+'→1,'*'→2)。
1) 左子表达式需要加括号的条件是:left_prec < prec_op
(也就是子表达式的根运算符优先级更低,一定要加括号)。
2) 右子表达式需要加括号的条件是:
- 子表达式的根运算符优先级更低: right_prec < prec_op
(相当于 “1 * (2+3)” 这种情况,子表达式是加法、优先级 < 乘法,必须加括号),
- 或者 子表达式的根运算符优先级等于当前运算符且 “当前运算符是左结合”:
right_prec == prec_op
(例如 “a + (b + c)” 里,右边如果也是 ‘+’,没有括号会变成 “a + b + c”
(即 (a+b)+c),这改变了原先“先算 b+c 再加 a”的顺序,必须保留)。
这样就能保证:在重建中缀字符串时,只加必要的括号,不会破坏原始运算顺序。
"""
stack = []
for tk in postfix:
if tk.isdigit():
# 数字本身优先级设为 3
stack.append((tk, 3))
else:
# tk 是 '+' 或 '*'
right_str, right_prec = stack.pop()
left_str, left_prec = stack.pop()
prec_op = 2 if tk == "*" else 1
# 左子表达式加括号的条件:left_prec < prec_op
if left_prec < prec_op:
left_str = f"({left_str})"
# 右子表达式加括号的条件:
# right_prec < prec_op 或
# right_prec == prec_op (因为 + 和 * 均为左结合,
# 同级右侧子表达式必须加括号)
if right_prec < prec_op or right_prec == prec_op:
right_str = f"({right_str})"
merged = f"{left_str}{tk}{right_str}"
stack.append((merged, prec_op))
return stack.pop()[0]
def simplify_expression(expr):
# 用正则把多位数字和运算符/括号拆开
tokens = re.findall(r'\d+|[()+*]', expr)
postfix = infix_to_postfix(tokens)
return postfix_to_infix(postfix)
# 主流程:读每一行、输出简化后的结果
if __name__ == "__main__":
import sys
for line in sys.stdin:
line = line.strip()
if not line:
continue
print(simplify_expression(line))python
import sys
# 运算符优先级和结合性(左结合 = 'L',右结合 = 'R')
PRECEDENCE = {
'+': (1, 'L'),
'-': (1, 'L'),
'*': (2, 'L'),
'/': (2, 'L'),
'NEG': (3, 'R'), # 一元负号
}
class Node:
def __init__(self, value, left=None, right=None):
self.value = value # 运算符或数值
self.left = left
self.right = right
def is_leaf(self):
return self.left is None and self.right is None
def tokenize(expr):
tokens = []
i = 0
while i < len(expr):
if expr[i].isspace():
i += 1
elif expr[i] in '+-*/()':
tokens.append(expr[i])
i += 1
elif expr[i].isdigit():
num = ''
while i < len(expr) and expr[i].isdigit():
num += expr[i]
i += 1
tokens.append(num)
else:
raise ValueError(f"Invalid character: {expr[i]}")
return tokens
def parse_expr(tokens):
i = 0
def parse_primary():
nonlocal i
if tokens[i] == '-':
# 一元负号
i += 1
return Node('NEG', right=parse_primary())
elif tokens[i] == '(':
i += 1
node = parse_add_sub()
i += 1 # skip ')'
return node
else:
val = tokens[i]
i += 1
return Node(val)
def parse_unary():
return parse_primary()
def parse_mul_div():
nonlocal i
node = parse_unary()
while i < len(tokens) and tokens[i] in ('*', '/'):
op = tokens[i]
i += 1
node = Node(op, node, parse_unary())
return node
def parse_add_sub():
nonlocal i
node = parse_mul_div()
while i < len(tokens) and tokens[i] in ('+', '-'):
op = tokens[i]
i += 1
node = Node(op, node, parse_mul_div())
return node
return parse_add_sub()
def to_string(node, parent_op=None, is_right=False):
if node.is_leaf():
return node.value
if node.value == 'NEG':
expr = to_string(node.right, 'NEG', True)
if not node.right.is_leaf() and PRECEDENCE.get(node.right.value, (0,))[0] < PRECEDENCE['NEG'][0]:
expr = f'({expr})'
return f'-{expr}'
left_expr = to_string(node.left, node.value, False) if node.left else ''
right_expr = to_string(node.right, node.value, True)
def need_paren(child, parent, is_right_child):
if child.is_leaf():
return False
child_prec, _ = PRECEDENCE.get(child.value, (0, 'L'))
parent_prec, parent_assoc = PRECEDENCE.get(parent, (0, 'L'))
if child_prec < parent_prec:
return True
if child_prec == parent_prec:
if parent_assoc == 'L' and is_right_child:
return True
if parent_assoc == 'R' and not is_right_child:
return True
return False
if need_paren(node.left, node.value, False):
left_expr = f'({left_expr})'
if need_paren(node.right, node.value, True):
right_expr = f'({right_expr})'
return f'{left_expr}{node.value}{right_expr}'
def simplify_expression(expr):
tokens = tokenize(expr)
ast = parse_expr(tokens)
return to_string(ast)
if __name__ == '__main__':
for line in sys.stdin:
line = line.strip()
if line:
print(simplify_expression(line))