Leetcode 第388场周赛 问题和解法

问题

字符串及其反转中是否存在同一子字符串

给你一个字符串s,请你判断字符串s是否存在一个长度为2的子字符串,在其反转后的字符串中也出现。

如果存在这样的子字符串,返回true;如果不存在,返回false。

解题思路

将字符串倒序排之后,每次截取两个字符判断是否存在题意中的字符串。

class Solution {
    public boolean isSubstringPresent(String s) {
        boolean res=false;
        int n=s.length();
        if (n<2){
            return res;
        }
        String re=new StringBuffer(s).reverse().toString();
        for(int i=0;i<n-1;i++){
            if (re.contains(s.substring(i,i+2))){
                return true;
            }
        }
        return res;
    }
}

统计以给定字符开头和结尾的子字符串总数

给你一个字符串s和一个字符c。返回在字符串s中并且以c字符开头和结尾的非空子字符串的总数。

解题思路

按题意推出只有1个字符时,出现次数是1;
只有两个字符时,出现次数是2;
只有三个字符时,出现次数是6;
有n次个字符时,出现次数是res[n-1]+k;

class Solution {
    public long countSubstrings(String s, char c) {
        long res=0;
        int n=s.length();
        int count=0;
        for(int i=0;i<n;i++){
            if (s.charAt(i)==c){
                count++;
            }
        }
        if (count==0){
            return res;
        }
        if (count==1){
            return 1;
        }
        if (count==2){
            return 3;
        }
        int right=3;
        res=3;
        for(int i=3;i<=count;i++){
            res=res+right;
            right++;
        }
        return res;
    }
}

成为 K 特殊字符串需要删除的最少字符数

给你一个字符串word和一个整数k。

如果|freq(word[i])-freq(word[j])|<=k对于字符串中所有下标i和j都成立,则认为word是k特殊字符串。

此处,freq(x)表示字符x在word中的出现频率,而|y|表示y的绝对值。

返回使word成为k特殊字符串需要删除的字符的最小数量。

解题思路

统计word中每个字母的出现次数,记到一个数组cnt中。

枚举i作为出现次数最小的字母,为了保留尽量多的字母,字母i肯定不需要删除。此外,出现次数最多的字母,其出现次数不能超过cnt[i]+k。

class Solution {
    public int minimumDeletions(String word, int k) {
        int[] cnt = new int[26];
        for (char c : word.toCharArray()) {
            cnt[c - 'a']++;
        }
        Arrays.sort(cnt);

        int maxSave = 0;
        for (int i = 0; i < 26; i++) {
            int sum = 0;
            for (int j = i; j < 26; j++) {
                sum += Math.min(cnt[j], cnt[i] + k); // 至多保留 cnt[i]+k 个
            }
            maxSave = Math.max(maxSave, sum);
        }
        return word.length() - maxSave;
    }
}

拾起 K 个 1 需要的最少行动次数

给你一个下标从0开始的二进制数组nums,其长度为n;另给你一个正整数k以及一个非负整数maxChanges。

灵茶山艾府在玩一个游戏,游戏的目标是让灵茶山艾府使用最少数量的行动次数从nums中拾起k个1。游戏开始时,灵茶山艾府可以选择数组[0,n-1]范围内的任何索引index站立。如果nums[index]==1,灵茶山艾府就会拾起一个1,并且nums[index]变成0(这不算作一次行动)。之后,灵茶山艾府可以执行任意数量的行动(包括零次),在每次行动中灵茶山艾府必须恰好执行以下动作之一:

选择任意一个下标j!=index且满足nums[j]==0,然后将nums[j]设置为1。这个动作最多可以执行maxChanges次。
选择任意两个相邻的下标x和y(|x-y|==1)且满足nums[x]==1,nums[y]0,然后交换它们的值(将nums[y]=1和nums[x]=0)。如果yindex,在这次行动后灵茶山艾府拾起一个1,并且nums[y]变成0。
返回灵茶山艾府拾起恰好k个1所需的最少行动次数。

解题思路

先用dylanIndex和其相邻的1,还有maxChanges来计算能不能取到k个1。如果可以那么其中的最小操作数量就是答案。如果取不了k个人,用滑动窗口计算取k-maxChanges要用到的最小操作(move)数量,加上2*maxChanges之后的最小操作数量就为答案。

class Solution {
    public long minimumMoves(int[] nums, int k, int maxChanges) {
		long res = Long.MAX_VALUE;
		int n = nums.length;
		long[] ps = new long[n];
		int[] oct = new int[n];
		long p = 0;
		int pc = 0;
		for (int i = 0; i < n; i++) {
			if (nums[i] == 1) {
				p += i;
				pc++;
			}
			oct[i] = pc;
			ps[i] = p;
		}

		for (int i = 0; i < n; i++) {
			int t = k, op = 0;
			if (nums[i] == 1)
				t--;
			if (t == 0) {
				res = Math.min(res, op);
				continue;
			}
			if (i != 0 && nums[i - 1] == 1) {
				op++;
				t--;
			}
			if (t == 0) {
				res = Math.min(res, op);
				continue;
			}
			if (i != n - 1 && nums[i + 1] == 1) {
				op++;
				t--;
			}
			if (t == 0) {
				res = Math.min(res, op);
				continue;
			}
			if (maxChanges >= t)
				res = Math.min(res, op + t * 2);
		}

		int lt = 0, ct = 0;
		int rk = k - maxChanges;
		if (res == Long.MAX_VALUE)
			for (int i = 0; i < n; i++) {
				if (nums[i] == 1) {
					ct++;
					if (ct >= rk) {
						while (nums[lt] == 0 || ct > rk) {
							if (nums[lt] == 1)
								ct--;
							lt++;
						}
						int l1 = lt, r1 = i;
						int m;
						while (l1 < r1) {
							m = l1 + r1 >> 1;
							if (skCount(oct, lt, m) < skCount(oct, m, i))
								l1 = m + 1;
							else
								r1 = m;
						}
						m = l1;
						long t1 = skCount(ps, m + 1,i ) - (long) skCount(oct, m + 1,i ) * m;
						long t2 = (long) (skCount(oct, lt,m )) * m - skCount(ps, lt,m );
						res = Math.min(res, t1 + t2 + 2 * maxChanges);
					}
				}
			}
		return res;
	}

	public int skCount(int[] oct, int f, int t) {
		return oct[t] - (f == 0 ? 0 : oct[f - 1]);
	}

	public long skCount(long[] oct, int f, int t) {
		return oct[t] - (f == 0 ? 0 : oct[f - 1]);
	}
}

来源

LeetCode 第 389 场周赛

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴代庄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值