生成特殊数字的最少操作

2024-07-25

题目:

给你一个下标从 0 开始的字符串 num ,表示一个非负整数。在一次操作中,您可以选择 num 的任意一位数字并将其删除。请注意,如果你删除 num 中的所有数字,则 num 变为 0

返回最少需要多少次操作可以使 num 变成特殊数字。如果整数 x 能被 25 整除,则该整数 x 被认为是特殊数字。

示例 1:

输入:num = "2245047"
输出:2
解释:删除数字 num[5] 和 num[6] ,得到数字 "22450" ,可以被 25 整除。
可以证明要使数字变成特殊数字,最少需要删除 2 位数字。

示例 2:

输入:num = "2908305"
输出:3
解释:删除 num[3]、num[4] 和 num[6] ,得到数字 "2900" ,可以被 25 整除。
可以证明要使数字变成特殊数字,最少需要删除 3 位数字。

示例 3:

输入:num = "10"
输出:1
解释:删除 num[0] ,得到数字 "0" ,可以被 25 整除。
可以证明要使数字变成特殊数字,最少需要删除 1 位数字。

提示

  • 1 <= num.length <= 100
  • num 仅由数字 '0''9' 组成
  • num 不含任何前导零

技巧枚举

思路:

// 思路1:暴力枚举所有 num 的删除情况 (删1个,2个 ... 判断是不是25的倍数)  时间复杂度:非常大
//思路2:从25的倍数 target 出发,寻找num要达到 target 最少的删除次数 (停止条件为25 的倍数 target > num),那其实就是判断target是不是num的子序列。这样时间复杂度就下降了

代码:

class Solution {
public:
    int isChild(string num, string target) {
        int i = 0, j = 0;
        while (i < num.size()) {
            if (num[i] == target[j]) {
                j++;
            }
            i++;
        }
        return j == target.size() ? num.size() - target.size() : -1;
    }
    int minimumOperations(string num) {
        int n = num.size(), res;
        long long cur = 25;
        int zero_idx = num.find('0');
        if (zero_idx == string::npos) {
            res = n;
        }else {
            res = n - 1;
        }
        while (true) {
            string target = to_string(cur);
            if (target.size() > num.size()) {
                break;
            } 
            int ret = isChild(num, target);
            if (ret != -1) {
                res = min(res, ret);
            }
            cur += 25;
        }
        return res;
    }
};

贪心算法

思路:

一个数字要想被 25 整除,它需要满足以下条件:
	如果它的位数为 1,则它必须是 0。
	如果它的位数为 2,则它必须是 25,50 或者 75。
	如果它的位数为大于等于 3,则它必须以 00,25,50 或者 75结尾。
我们需要将字符串 num 进行最少步数的操作,使其满足以上条件之一。题目又规定了 num 不包含前导 0,因此不可能存在经过最少步数的操作,使得 num 变为 00,所以这一特殊情况不在以上条件的讨论范围里。

我们从右至左遍历 num,
	如果遇到 0 或 5:
		如果在这之前遇到过 0,则将这之前的 0,当前的数字,以及当前的数字以左的数字都保留,其他的数字删除。记 num 长度为 n,当前下标为 i,最少操作数即为 n − i − 2。
		如果在这之前没有遇到过 0,则标记一下状态,表示遇到了 0 或 5。
	如果遇到 2 或 7:
		如果在这之前遇到过 5,则将这之前的 5,当前的数字,以及当前的数字以左的数字都保留,其他的数字删除。记 num 长度为 n,当前下标为 i,最少操作数即为 n − i − 2。
		如果遍历完都没有找到最少操作数,那么说明我们不可能通过操作来使得 num 变得以 00,25,50 或者 75 结尾。

如果我们在遍历中遇到了 0,那么我们就删除其他所有数字,只保留这个 0,返回 n − 1。
否则,我们将所有数字删除,返回 n。

代码:

class Solution {
public:
    int minimumOperations(string num) {
        int n = num.size();
        bool find0 = false, find5 = false;
        for (int i = n - 1; i >= 0; i--) {
            if (num[i] == '0' || num[i] == '5') {
                if (find0) {
                    return n - i - 2;
                }
                if (num[i] == '0') {
                    find0 = true;
                }else {
                    find5 = true;
                }
            }
            else if (num[i] == '2' || num[i] == '7') {
                if (find5) {
                    return n - i - 2;
                }
            }
        }
        if (find0) {
            return n - 1;
        }
        return n;
    }
};