题目地址:
https://www.lintcode.com/problem/combine-new-numbers/description
给定一个非负整数数组 A A A,要求从其中取出一些数字做拼接,使得拼接出的数是 3 3 3的倍数。问满足条件的最大数是多少。
一个数能被
3
3
3整除当且仅当其各个位之和是
3
3
3的倍数。先算一下
A
A
A中所有数字之和
s
s
s,如果其是
3
3
3的倍数,那我们全要。否则我们肯定希望删除的数越短越好(这样拼起来的数的位数尽量多),如果一样长,则希望删除的数越小越好。如果
s
≡
1
(
m
o
d
3
)
s\equiv 1(\mod 3)
s≡1(mod3),那么我们只需要删掉
A
A
A中"最小"的一个模
3
3
3余
1
1
1的数即可,如果这样的数不存在,我们需要删掉"最小"的两个模
3
3
3余
2
2
2的数;同理,如果
s
≡
2
(
m
o
d
3
)
s\equiv 2(\mod 3)
s≡2(mod3),那我们只需要删"最小"的一个模
3
3
3余
2
2
2的数,如果没有,则删"最小"的两个模
3
3
3余
1
1
1的数。所以我们还需要开一个数组记录模
3
3
3余
0
,
1
,
2
0,1,2
0,1,2的数字个数分别是多少。然后根据
s
s
s模
3
3
3的情况决定删几个模
3
3
3余几的数。
所以关于删除哪些数,我们的方案是,先将所有数排序,位数少的在前,如果位数一样,那么数值小的在前。排完序后,将最小的要删除的数删除,然后再排一次序,这次是将每个数视为字符串,按照如果
s
+
t
s+t
s+t字典序小于
t
+
s
t+s
t+s,则
s
s
s排在
t
t
t之后(参考https://blog.csdn.net/qq_46105170/article/details/105324262)。排完之后用一个StringBuilder一个个append上去即可。代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
/**
* @param A: the array in problem.
* @return: represent the new number.
*/
public String Combine(List<Integer> A) {
// write your code here
String[] strs = new String[A.size()];
int sum = 0;
int[] mod3 = new int[3];
for (int i = 0; i < A.size(); i++) {
int num = A.get(i);
sum += num;
mod3[num % 3]++;
strs[i] = String.valueOf(num);
}
// 对A中的数字(视为字符串)排序,长度短的在前,长度一样则数值小的在前
Arrays.sort(strs, (s1, s2) -> {
if (s1.length() != s2.length()) {
return Integer.compare(s1.length(), s2.length());
} else {
return s1.compareTo(s2);
}
});
// 算一下要删模3余多少的多少个数
int delCount = 0, mod = 0;
if (sum % 3 == 1) {
if (mod3[1] > 0) {
delCount = mod = 1;
} else {
delCount = 2;
mod = 2;
}
} else if (sum % 3 == 2) {
if (mod3[2] > 0) {
delCount = 1;
mod = 2;
} else {
delCount = 2;
mod = 1;
}
}
// 删掉delCount个模3等于mod的最小数(这里指的是按上面的排序最小的)
for (int i = 0; i < strs.length; i++) {
String n = strs[i];
if (delCount > 0 && Integer.parseInt(n) % 3 == mod) {
delCount--;
// 这里删除的方法是置为空串
strs[i] = "";
}
if (delCount == 0) {
break;
}
}
// 再排一次序,这时候按照拼接谁应该在前能使得得数更大来排序
Arrays.sort(strs, (s1, s2) -> {
if (s1.isEmpty() || s2.isEmpty()) {
return Integer.compare(s1.length(), s2.length());
} else {
return -(s1 + s2).compareTo(s2 + s1);
}
});
StringBuilder sb = new StringBuilder();
for (int i = 0; i < strs.length; i++) {
sb.append(strs[i]);
}
// 去掉前导0
int i = 0;
while (i < sb.length() - 1 && sb.charAt(i) == '0') {
i++;
}
return sb.substring(i);
}
}
时间复杂度 O ( l n log n ) O(ln\log n) O(lnlogn),空间 O ( n l ) O(nl) O(nl), n n n是数字个数, l l l是数字最长长度。