Leetcode - 周赛390

文章介绍了四个编程问题,分别是:使用滑动窗口解决每个字符最多出现两次的最长子字符串,通过贪心策略执行操作使数据元素之和大于等于K,找出最高频率的ID以及通过字典树实现最长公共后缀查询。
摘要由CSDN通过智能技术生成

目录

一,3090. 每个字符最多出现两次的最长子字符串

二,3091. 执行操作使数据元素之和大于等于 K

三,3092. 最高频率的 ID

四,3093. 最长公共后缀查询


一,3090. 每个字符最多出现两次的最长子字符串

本题是一道标准的滑动窗口问题,代码如下: 

class Solution {
    public int maximumLengthSubstring(String s) {
        char[] ch = s.toCharArray();
        int[] cnt = new int[26];
        int ans = 0;
        for(int l=0, r=0; r<ch.length; r++){
            cnt[ch[r]-'a']++;
            while(cnt[ch[r]-'a']>2){
                cnt[ch[l]-'a']--;
                l++;
            }
            ans = Math.max(ans, r-l+1);
        }
        return ans;
    }
}

二,3091. 执行操作使数据元素之和大于等于 K

本题是一道贪心题,由题可知,只有先执行+1操作,再执行复制操作,它的操作次数才是最少的,又因为本题的数据范围不大,所以可以使用枚举的方式来求最小值,代码如下:

class Solution {
    public int minOperations(int k) {
        int ans = Integer.MAX_VALUE;
        for(int i=1; i<50000; i++){
            // i-1 表示 i 执行 +1 操作的次数
            // (k-i-1)/i+1 表示执行 复制 操作的次数
            ans = Math.min(ans, (i-1)+(k-i-1)/i+1);
        }
        return ans;
    }
}

上述的公式可以化简成 i + (k-1)/i - 1,从数学的角度来看,这就是 y = x + (k-1)/x + c 函数,所以由函数特点可知,要使 y 最小,x = k-1开根号,又因为题目中的 x 必须为正整数,所以需要求 x 左右两侧的正整数。函数图像:

代码如下:

class Solution {
    public int minOperations(int k) {
        int rt = Math.max((int)Math.sqrt(k-1),1);
        return Math.min(rt-1+(k-1)/rt, rt+(k-1)/(rt+1));
    }
}

三,3092. 最高频率的 ID

本题求的是ID出现次数最多的出现次数,注意返回的是出现次数,不是ID,这里有两种写法:

1)哈希表 + 有序集合

使用哈希表存储 < ID,ID出现次数 >,使用有序集合存储 < ID出现次数,ID出现次数的次数>,根据题目要求进行模拟,代码如下:

class Solution {
    public long[] mostFrequentIDs(int[] nums, int[] freq) {
        int n = nums.length;
        Map<Integer, Long> map = new HashMap<>();
        TreeMap<Long, Integer> tree = new TreeMap<>();
        long[] ans = new long[n];
        for(int i=0; i<n; i++){
            int x = nums[i];
            if(map.containsKey(x) && tree.containsKey(map.get(x))){
                if(tree.get(map.get(x))-1 == 0)
                    tree.remove(map.get(x));
                else
                    tree.put(map.get(x), tree.get(map.get(x))-1);
            }
            map.put(x, map.getOrDefault(x,0L)+freq[i]);
            tree.put(map.get(x), tree.getOrDefault(map.get(x),0)+1);
            ans[i] = tree.lastKey();
        }
        return ans;
    }
}

2)哈希表 + 懒删除堆

和上面那种方法一样,只不过这里使用堆来维护最大的出现次数,此外堆里存储的内容和上文不同,堆里存储的是 < ID出现的次数,ID >,(注:这里使用最大堆,可以通过pop直接得到最大值,如果使用最小堆可能会超时)代码如下:

class Solution {
    public long[] mostFrequentIDs(int[] nums, int[] freq) {
        int n = nums.length;
        long[] ans = new long[n];
        Map<Long, Long> map = new HashMap<>();
        PriorityQueue<Long[]> que = new PriorityQueue<>((x,y)->Long.compare(y[0],x[0]));
        for(int i=0; i<n; i++){
            long x = nums[i];
            map.put(x, map.getOrDefault(x,0L)+freq[i]);
            que.offer(new Long[]{map.get(x), x});
            while(!map.get(que.peek()[1]).equals(que.peek()[0])){
                que.poll();
            }
            ans[i] = que.peek()[0]; 
        }
        return ans;
    }
}

四,3093. 最长公共后缀查询

本题求最长公共后缀的数组下标,是一道典型的字典树题,不懂的可以看Leetcode - 周赛385那篇博客,节点中需要存储的内容需要变一下,存储一下数组长度及其下标,代码如下:

class Solution {
    static class Node{
        Node[] node = new Node[26];
        int len;//最短长度
        int idx;//下标
    }
    public int[] stringIndices(String[] wordsContainer, String[] wordsQuery) {
        int n = wordsContainer.length;
        Node root = new Node();
        int min = wordsContainer[0].length();
        int k = 0;//求如果没有公共后缀时,它的结果为最短长度&最靠前的数组下标

        //后缀字典树
        for(int i=0; i<n; i++){
            String s = wordsContainer[i];
            if(min > s.length()){
                min = s.length();
                k = i; 
            }
            char[] ch = s.toCharArray();
            Node cur = root;
            for(int j=ch.length-1; j>=0; j--){
                int index = ch[j]-'a';
                if(cur.node[index]==null){
                    cur.node[index] = new Node();
                    cur.node[index].len = s.length();
                    cur.node[index].idx = i;
                }
                if(cur.node[index].len > s.length()){
                    cur.node[index].len = s.length();
                    cur.node[index].idx = i;
                }
                cur = cur.node[index];
            }
        }
        int m = wordsQuery.length;
        int[] ans = new int[m];
        Arrays.fill(ans, -1);
        for(int i=0; i<m; i++){
            Node cur = root;
            String s = wordsQuery[i];
            char[] ch = s.toCharArray();
            for(int j=ch.length-1; j>=0; j--){
                int index = ch[j]-'a';
                if(cur.node[index]==null){
                    if(ans[i] == -1)
                        ans[i] = k;
                    break;
                }else{
                    ans[i] = cur.node[index].idx;
                    cur = cur.node[index];
                }
            }
        }
        return ans;
    }
}

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一叶祇秋

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

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

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

打赏作者

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

抵扣说明:

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

余额充值