题目
特殊的二进制序列是具有以下两个性质的二进制序列:
- 0 的数量与 1 的数量相等。
- 二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量。
给定一个特殊的二进制序列 S,以字符串形式表示。定义一个操作 为首先选择 S 的两个连续且非空的特殊的子串,然后将它们交换。(两个子串为连续的当且仅当第一个子串的最后一个字符恰好为第二个子串的第一个字符的前一个字符。)
在任意次数的操作之后,交换后的字符串按照字典序排列的最大的结果是什么?
示例 1:
输入: S = "11011000"
输出: "11100100"
解释:
将子串 "10" (在S[1]出现) 和 "1100" (在S[3]出现)进行交换。
这是在进行若干次操作后按字典序排列最大的结果。
说明:
S 的长度不超过 50。
S 保证为一个满足上述定义的特殊 的二进制序列。
解题思路
分治 递归
将1看做左括号,0看做右括号,11011000=(()(()))
由题中所知,输入为一个特殊二进制序列,每次只能将连续挨着的两个特殊二进制序列进行交换
可知:
①输入的特殊二进制序列一定是1开始,0结尾,中间包含子串
②中间的子串一定是包含了特殊二进制序列一个或多个
可做转化为:
- 对1…0中的特殊二进制序列子串进行冒泡排序
- 由于冒泡排序是挨着的元素进行比较排序(符合题目的交换规则),每一轮排序确定一个最大或最小的固定
- 元素(即字符串a,b)的比较规则为:从前往后每个字符比较,a[i]>b[i] 则a>b,a放b前面
如此转化后可以将当前输入字符串的特殊二进制序列子串排序完成,但其特殊二进制序列子串还可以进行如上的排序
因此进行递归处理
注意,对于当前串A={a,b,c,d,e…},先分治递归得到子串a\b\c\d\e…的最优序列,再进行A的子串排序得到最优序列。
代码
class Solution {
public:
/*
* **递归**
* 将1看做左括号,0看做右括号,11011000=(()(()))
* 由题中所知,输入为一个特殊二进制序列,每次只能将连续挨着的两个特殊二进制序列进行交换
* 可知:
* ①输入的特殊二进制序列一定是1开始,0结尾,中间包含子串
* ②中间的子串一定是包含了特殊二进制序列一个或多个
* 可做转化为:
* - 对1...0中的特殊二进制序列子串进行冒泡排序
* - 由于冒泡排序是挨着的元素进行比较排序(符合题目的交换规则),每一轮排序确定一个最大或最小的固定
* - 元素(即字符串a,b)的比较规则为:从前往后每个字符比较,a[i]>b[i] 则a>b,a放b前面
* 如此转化后可以将当前输入字符串的特殊二进制序列子串排序完成,但其特殊二进制序列子串还可以进行如上的排序
* 因此进行递归处理
* 注意,对于当前串A={a,b,c,d,e...},先分治递归得到子串a\b\c\d\e...的最优序列,再进行A的子串排序得到最优序列。
*/
string makeLargestSpecial(string s) {
if(s.length() < 3) return s;
// 进行子串提取
vector<string> arr;
int score = 0;
string temp = "";
for(int i = 0; i < s.length(); i++){
// 对1...0中间子串挨个处理,反向处理防止出现非1开始的串但开销为0,如)(的开销为0
temp += s[i];
if(s[i] == '1') score++;
else{
score --;
// 控制必须在0的结尾时候才能加入
if(score == 0){
// 注意在当前子串排序前先递归,获得子串最佳的序列后再排序
arr.push_back("1" + makeLargestSpecial(temp.substr(1,temp.length()-2) ) + "0");
temp = "";
}
}
}
// 排序,和冒泡排序的结果等价
sort(arr.begin(), arr.end(), greater<string>());
// 进行递归处理
string ans = "";
for(int i = 0;i < arr.size(); i++){
cout<<arr[i]<<" ";
ans += arr[i];
}
cout<<endl;
return ans;
}
};