LWC 66: 761. Special Binary String
传送门:761. Special Binary String
Problem:
Special binary strings are binary strings with the following two properties:
The number of 0’s is equal to the number of 1’s.
Every prefix of the binary string has at least as many 1’s as 0’s.
Given a special string S, a move consists of choosing two consecutive, non-empty, special substrings of S, and swapping them. (Two strings are consecutive if the last character of the first string is exactly one index before the first character of the second string.)At the end of any number of moves, what is the lexicographically largest resulting string possible?
Example 1:
Input: S = “11011000”
Output: “11100100”
Explanation:
The strings “10” [occuring at S1] and “1100” [at S[3]] are swapped.
This is the lexicographically largest string possible after some number of swaps.
Note:
- S has length at most 50.
- S is guaranteed to be a special binary string as defined above.
思路:
唉,到底是自己英文不够好,还是题目意思含糊不清啊。对于Special binary string 的定义实际上是这样的:
1. 字符串0和1出现的次数相等
2. 非常关键,字符串开始一定是以1开头,且前缀1出现的次数至少与0出现的次数相等。比如”11100100”是special binary string,因为1出现的次数始终大于等于0。
写个isSpecial的判断函数吧,这就一目了然了。
public boolean isSpecial(String S){
char[] cs = S.toCharArray();
int count = 0;
for(int i = 0; i < S.length(); i++){
if (cs[i] == '1') count ++;
else count --;
if (count < 0) return false;
}
return count == 0;
}
OK,因为是连续两个special binary string位置的交换,所以我们可以遍历S的每个位置,求出每个位置可能的special binary string,用map记录。接着交换任意两个连续位置的special binary string,取lexicographically最大的。不过这还不算完,注意:At the end of any number of moves,所以还需要迭代一波,直到max不再变化为止,即找到了最大值。很奇怪,为什么max不再变化就说明取到了最大值呢?我只能说,因为上述算法就是单调递增的。。。
Java版本:
public String makeLargestSpecial(String S) {
String max = new String(S);
int n = S.length();
Map<Integer, List<String>> map = new HashMap<>();
char[] cs = S.toCharArray();
for (int i = 0; i < n; ++i) {
if (cs[i] == '1') {
int j = i + 1 + 1;
while (j <= n) {
if (isSpecial(S.substring(i, j)))
map.computeIfAbsent(i, k -> new ArrayList<>()).add(S.substring(i, j));
j += 2;
}
}
}
for (int i : map.keySet()) {
List<String> firs = map.get(i);
for (String fir : firs) {
int j = i + fir.length();
if (map.containsKey(j)) {
for (String sec : map.get(j)) {
String cnd = swap(S, i, fir.length(), j, sec.length());
if (max.compareTo(cnd) < 0) {
max = cnd;
}
}
}
}
}
if (max.equals(S)) return max;
else return makeLargestSpecial(max);
}
String swap(String S, int i, int n1, int j, int n2) {
String ans = S.substring(0, i);
ans += S.substring(j, j + n2);
ans += S.substring(i + n1, j);
ans += S.substring(i, i + n1);
ans += S.substring(j + n2);
return ans;
}
public boolean isSpecial(String S){
char[] cs = S.toCharArray();
int count = 0;
for(int i = 0; i < S.length(); i++){
if (cs[i] == '1') count ++;
else count --;
if (count < 0) return false;
}
return count == 0;
}
再来一种聪明的做法,herustic一下,实际上按照special binary string的定义,首字母一定是”1”,末尾一定是”0”,这两位是我们无法改变的,所以我们完全可以求解子问题:除了首尾的字符串。各位且慢,举个例子”1010”,是special binary string,首尾的确分别是1和0,但很遗憾”01”并不是special binary string啊,那怎么用递归解决啊!所以我们需要确保找到”1A….B0”之后,”A…B”也是special binary string。 很简单,只要第一次出现count == 0的字符串。它除了首尾的子串一定是special binary string。证明:因为”1A….CB0” count = 0,所以,”1A….CB”时,count = 1,如果”B = 1”,只能为”1A…C” ,count = 0,这就提前出现了count = 0的情况,吼吼,与假设矛盾。
嘿,既然能够找到第一个count = 0的special binary string,并且确保了子问题也是special binary string,就可以递归求解了。不过,最终还需要输出lexicographically,所以排个序再合并之前分解的special binary string.
代码如下:
public String makeLargestSpecial(String S) {
int count = 0, i = 0;
List<String> res = new ArrayList<String>();
for (int j = 0; j < S.length(); ++j) {
if (S.charAt(j) == '1') count++;
else count--;
if (count == 0) {
res.add('1' + makeLargestSpecial(S.substring(i + 1, j)) + '0');
i = j + 1;
}
}
Collections.sort(res, Collections.reverseOrder());
return String.join("", res);
}
Python版本:
class Solution(object):
def makeLargestSpecial(self, S):
"""
:type S: str
:rtype: str
"""
count = i = 0
res = []
for j, v in enumerate(S):
count = count + 1 if v == '1' else count - 1
if count == 0:
res.append('1' + self.makeLargestSpecial(S[i + 1 : j]) + '0')
i = j + 1
return ''.join(sorted(res)[::-1])