【Lintcode】1341. Combine New Numbers

题目地址:

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) s1(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) s2(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是数字最长长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值