【刷题笔记】第一天

两道贪心题

3106. 满足距离约束且字典序最小的字符串

字典序:abcd顺序,其中z和a的差值是1。

distance(s, t)表示对应位置字符的ASCLL差的累加。

例如distance(“ab”, “cd”) == 4 (c - a + d - b )

当k等于0时,表示s和t完全一样

当k等于1时,表示只有s[i] != t[i],且s[i] - t[i] == 1

所以相当于求对字符串s操作k次,每次k减一,字符移动一位,最小字典项的字符串是什么。
尽量使得每一个位置的字符为a

class Solution {
    public String getSmallestString(String s, int k) {
        // 要求字典序最小,肯定是从前往后调整
        char[] chs = s.toCharArray();
        for (int i = 0; i < chs.length; ++i) {
            char c = chs[i];
            int dis = Math.min(c - 'a', 'z' + 1 - c);
            if (dis > k) {
                // 当c距离a的最小距离超过k时,直接令c往前跳k步,为什么不是往后跳呢?
				// 因为两者的最小值都大于k了,往后跳肯定跳不到a
                chs[i] = (char)(chs[i] - k); // 往前跳是为了保证字典项最小
                break;
            }
            k -= dis;
            chs[i] = 'a';
        }
        return new String(chs);

    }
}

3107. 使数组中位数等于 K 的最少操作数

中位数小于K

image-20240410110804339

中位数大于K

image-20240410110858860

class Solution {
    public long minOperationsToMakeMedianK(int[] nums, int k) {
        int mid = nums.length / 2;
        Arrays.sort(nums); 
        long ans = 0;
        if (nums[mid] > k) { // nums[mid]表示当前数组的中位数
            // 将中间左边的位置置为小于等于k
            // 为什么不需要管中间右边的元素呢,因为右边的元素一定大于等于k
            // 为什么只需要设置为k呢,因为题目要求操作次数最小
            for (int i = mid; i >= 0; i--) {
                if (nums[i] <= k) break;
                ans += nums[i] - k;
            }
        } else if (nums[mid] < k) {
            // 将中间右边的元素置为大于等于k
            for (int i = mid; i < nums.length; ++i) {
                if (nums[i] >= k) break;
                ans += k - nums[i];
            }
        }
        return ans;

    }
}

思考:改成输入 $10^5 $个询问,每个询问包含一个 k k k,如何高效地回答每个询问?

首先也是先排序,然后判断中位数是否大于k

如果中位数大于k:就二分找到第一个大于k的位置,假设是i,从i位置到mid位置所有元素都变为k,所需要的操作次数= sum[mid] - sum[i - 1] - (mid - i + 1) * k,其中sum是前缀和数组。

class Solution {
 public long minOperationsToMakeMedianK(int[] nums, int k) {
     int mid = nums.length / 2;
     Arrays.sort(nums);
     long ans = 0;
     long[] sum = new long[nums.length];
     sum[0] = nums[0];
     for (int i = 1; i < nums.length; ++i) {
         sum[i] = sum[i - 1] + nums[i];
     }
     if (nums[mid] > k) {

         // 将中间左边的位置置为小于等于k
         // 为什么不需要管中间右边的元素呢,因为右边的元素一定大于等于k
         // 为什么只需要设置为k呢,因为操作次数最小
         int l = 0, r = nums.length - 1;
         while (l < r) {
             int m = l + r >> 1;
             if (nums[m] > k) {
                 r = m;
             } else {
                 l = m + 1;
             }
         }
         if (l <= mid) {
             if (l == 0) {
                 ans += sum[mid] - ((long)mid - l + 1) * k;
             } else {
                 ans += sum[mid] - sum[l - 1] - ((long)mid - l + 1) * k;
             }

         }
     } else if (nums[mid] < k) {
         // 将中间右边的元素置为大于等于k
         int l = 0, r = nums.length - 1;
         while (l < r) {
             int m = l + r + 1 >> 1;
             if (nums[m] < k) {
                 l = m;
             } else {
                 r = m - 1;
             }
         }

         if (l >= mid) {
             if (mid == 0) {
                 // l - mid + 1) * k可能会溢出,所以要强转为long
                 ans += ((long)l - mid + 1) * k - sum[l];
             } else {
                 ans += ((long)l - mid + 1) * k - (sum[l] - sum[mid - 1]);
             }
         } 
     }
     return ans;
 }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值