#P2274. 第2题-求最大括号表达式
-
1000ms
Tried: 480
Accepted: 158
Difficulty: 6
所属公司 :
华为
时间 :2024年10月16日-国内
-
算法标签>递归
第2题-求最大括号表达式
题目描述
给定一个合法的括号序列,要求通过对其进行若干次(或不进行)的相邻合法括号子序列的交换,得到字典序最大的括号序列。每个合法的括号序列由左右括号组成,并且是有效的括号匹配。
解题思路
这道题的核心在于递归拆分括号子序列,然后对子序列进行字典序排序。我们可以将括号表达式看作是树状的结构,递归处理括号内部的子结构并保证排序合并后的结果是字典序最大的表达式。
思路步骤
-
递归拆分括号序列:
- 对于给定的括号表达式,我们先从左到右遍历,使用一个计数器
cnt来跟踪当前括号的匹配状态。当计数器归零时,说明我们找到了一个完整的合法括号序列(即一对匹配的())。 - 然后我们可以将这个合法括号序列作为一个独立的子结构进行递归处理。
- 对于给定的括号表达式,我们先从左到右遍历,使用一个计数器
-
递归处理每个子序列:
- 对于每个合法括号序列,可以递归调用相同的函数进行处理。对于单一的括号序列(如
(())),可以去掉外层的括号,递归处理里面的内容;对于多个子括号序列(如()()),可以分别递归处理。
- 对于每个合法括号序列,可以递归调用相同的函数进行处理。对于单一的括号序列(如
-
字典序排序:
-
当多个子括号序列被递归处理后,按照
a + b和b + a的字典序进行比较,来决定两个子序列的拼接顺序。这种拼接保证了最终的括号表达式的字典序最大。 -
例如,如果两个子序列
a和b,如果a + b的字典序大于b + a,那么a应该排在b的前面。 -
Q:为什么这么排序?A:见LeetCode 179.最大数
-
-
合并排序后的结果:
- 在排序完成后,将这些子序列按顺序拼接起来,形成当前层次的括号表达式,返回给上一层递归。
代码
java
import java.util.*;
public class Main {
// 自定义比较器,用来比较两个字符串拼接的字典序
public static int compare(String a, String b) {
if ((a + b).compareTo(b + a) > 0) {
return 1;
} else if ((a + b).compareTo(b + a) < 0) {
return -1;
} else {
return 0;
}
}
// 深度优先搜索函数,递归处理括号表达式
public static String dfs(String s) {
if (s.length() == 0) {
return "";
}
List<String> part = new ArrayList<>();
StringBuilder now = new StringBuilder();
int cnt = 0;
// 分割括号表达式,按完整的括号对划分
for (char c : s.toCharArray()) {
if (c == '(') {
cnt++;
} else {
cnt--;
}
now.append(c);
if (cnt == 0) { // 找到一个完整的括号对
part.add(now.toString());
now = new StringBuilder();
}
}
List<String> subRes = new ArrayList<>();
if (part.size() == 1) {
// 如果只有一个完整的括号对,递归处理去掉最外层括号后的子表达式
subRes.add("(" + dfs(part.get(0).substring(1, part.get(0).length() - 1)) + ")");
} else {
// 递归处理多个括号对
for (String p : part) {
subRes.add(dfs(p));
}
}
// 排序子表达式,使得字典序最大
Collections.sort(subRes, (a, b) -> compare(a, b));
// 返回拼接后的结果
return String.join("", subRes);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next(); // 输入括号字符串
System.out.println(dfs(s)); // 输出处理后的结果
}
}
python
from functools import cmp_to_key
# 读取输入的括号字符串
s = input()
# 自定义比较函数,比较 a 和 b 的拼接顺序,决定字典序的顺序
# 如果 a + b > b + a,返回 1,表示 a 应该排在 b 的后面
# 如果 a + b < b + a,返回 -1,表示 a 应该排在 b 的前面
# 否则返回 0 表示相等
def compare(a, b):
if a + b > b + a:
return 1
elif a + b < b + a:
return -1
return 0
# 深度优先搜索 (DFS) 函数,用来递归处理括号表达式
def dfs(s):
# 如果当前字符串为空,直接返回空字符串
if len(s) == 0:
return ""
part = [] # 用于存储当前括号层的所有子括号表达式
now = "" # 临时变量,记录当前子表达式
cnt = 0 # 计数器,判断当前括号是否匹配
# 遍历字符串中的每个字符
for c in s:
if c == '(': # 遇到左括号时,计数器加 1
cnt += 1
else: # 遇到右括号时,计数器减 1
cnt -= 1
now += c # 将当前字符加入到子表达式中
# 当计数器为 0 时,说明当前子表达式是一个完整的括号对
if cnt == 0:
part.append(now) # 将完整的子表达式存入 part 列表中
now = "" # 重置 now,开始处理下一个子表达式
sub_res = [] # 存储处理后的子表达式结果
# 如果 part 只有一个完整的括号对
if len(part) == 1:
# 递归处理去掉最外层括号的子表达式,并将结果重新加上括号
sub_res.append("(" + dfs(part[0][1:-1]) + ")")
else:
# 否则递归处理每个子表达式
for p in part:
sub_res.append(dfs(p))
# 排序所有子表达式,按照自定义的 compare 函数
# 比较规则是,如果 a + b < b + a,则 a 应该排在 b 的前面
return ''.join(sorted(sub_res, key=cmp_to_key(compare)))
# 调用 dfs 函数,计算并输出字典序最大的括号表达式
print(dfs(s))
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 自定义比较器函数,比较两个字符串拼接后的字典序
bool compare(const string& a, const string& b) {
return a + b < b + a;
}
// 深度优先搜索函数,递归处理括号表达式
string dfs(string s) {
if (s.empty()) return "";
vector<string> part;
string now;
int cnt = 0;
// 分割括号表达式,按完整的括号对划分
for (char c : s) {
if (c == '(') cnt++;
else cnt--;
now += c;
if (cnt == 0) { // 找到一个完整的括号对
part.push_back(now);
now.clear();
}
}
vector<string> subRes;
if (part.size() == 1) {
// 如果只有一个完整的括号对,递归处理去掉最外层括号后的子表达式
subRes.push_back("(" + dfs(part[0].substr(1, part[0].size() - 2)) + ")");
} else {
// 递归处理多个括号对
for (string p : part) {
subRes.push_back(dfs(p));
}
}
// 排序子表达式,使得字典序最大
sort(subRes.begin(), subRes.end(), compare);
// 返回拼接后的结果
string result;
for (const string& str : subRes) {
result += str;
}
return result;
}
int main() {
string s;
cin >> s; // 输入括号字符串
cout << dfs(s) << endl; // 输出处理后的结果
return 0;
}
OJ会员可以通过点击题目上方《已通过》查看其他通过代码来学习。
题目内容
有效括号表达式定义:
1、空串和()均为有效表达式。
2、当A、B为有效表达式时,则(A)、AB也均是有效的括号表达式,比如:A为(),则 ()()和(())$均为有效括号表达式。
括号表达式的值:左括号用1表示,右括号用0表示,该二进制序列对应的值即为括号表达式的值。
现给定一个有效括号表达式,对其中任意两个相邻的子"有效表达式”进行交换,求在任意次数(包含0次)的交换之后,能够得到的值最大的括号表达式。
说明:
1、表达式自身是有效表达式。
2、交换的必须是相邻且有效的。
输入描述
给定一个有效括号表达式,只包含左右括号"()”。 表达式的长度不超过60。
输出描述
在任意次数(包含0次)的交换之后,能够得到的值最大的括号表达式
样例1
输入
((()(())))
输出
(((())()))
说明
将在s[2]出现的有效表达式“()”和在s[4]出现的有效表达式(())进行交换。 对应二进制: 11 10 1100 00转换为11 1100 10 00
样例2
输入
()()
输出
()()
说明
无需交换,交换后也一样
样例3
输入
()(())((()(())))
输出
(((())()))(())()
说明
交换s[8−9]与s[10−13],得到()(())(((())()))
交换s[2−5]与s[6−15],得到()(((())()))(())
交换s[0−1]与s[2−11],得到(((())()))()(())
交换s[10−11]与s[12−15],得到(((())()))(())()