以赛促练-力扣第85场双周赛以及第307场周赛

文章目录

  • 第85场双周赛
        • T3.字母移位II
        • T4.删除操作后的最大子段和
  • 第307场周赛
        • T2.最大回文数字
        • T3.感染二叉树需要的总时间
        • T4.找出数组的第K大和

第85场双周赛

T1直接暴力枚举所有k长度的区间,统计出修改的最小次数。

T2 调用Java String的replaceAll(String a, String b)方法秒杀

class Solution {
    public int secondsToRemoveOccurrences(String s) {
        int res=0;
        while(s.contains("01")){
            res++;
            s=s.replaceAll("01","10");
        }
        return res;
    }
}

T3.字母移位II

这题我是废了脑力的,刚开始想直接暴力,遍历shifts中的所有区间,把移位总数全部统计出来,最后进行移位,直接TLE,才发现这有可能是O(N^2)的时间复杂度。刚好前几天的每日一题,看到宫水三叶大佬提到了差分法,我的另一篇文章算法学习-差分也进行了整理,这道题就是满足「区间修改,单点查询」的性质,因此可以采用差分法做。

class Solution {
    public String shiftingLetters(String s, int[][] shifts) {
        int len=s.length();
        int[]c=new int[len+1];
        int[]change=new int[len];
        for(int[]sh:shifts){
            int start=sh[0],end=sh[1],v=sh[2];
            c[start]+=v==0?-1:1;
            c[end+1]+=v==0?1:-1;
        }
        change[0]=c[0];
        for(int i=1;i<len;i++){
            change[i]=change[i-1]+c[i];
        }
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<len;i++){
            int shiftnum=change[i]%26;
            int temp=s.charAt(i)+shiftnum;
            if(temp<'a'){
                temp='z'-(Math.abs(temp-'a')-1);
            }
            if(temp>'z'){
                temp='a'+(Math.abs(temp-'z')-1);
            }
            sb.append((char)temp);
        }
        return sb.toString();
    }
}

其中可以简化的点是,差分数组的求和可以放在最后遍历改变字符串s的循环中,同时这种循环移位(即z右移变为aa左移变为z)也可以进行简化,即计算相对于a的偏移量:

class Solution {
    public String shiftingLetters(String s, int[][] shifts) {
        int len=s.length();
        int[]c=new int[len+1];
        int[]change=new int[len];
        for(int[]sh:shifts){
            int start=sh[0],end=sh[1],v=sh[2];
            c[start]+=v==0?-1:1;
            c[end+1]+=v==0?1:-1;
        }
        StringBuilder sb=new StringBuilder();
        for(int i=0,shiftNum=0;i<len;i++){
            shiftNum+=c[i];
            int gap=s.charAt(i)-'a';
            //+26可以让负数变为相对于'a'的正数,对于正数却无影响
            int newGap=((gap+shiftNum)%26+26)%26;
            char res=(char)(newGap+'a');
            sb.append(res);
        }
        return sb.toString();
    }
}

在计算循环移位字母的时候下面的trick可以重点记住:

int newGap=((gap+shiftNum)%26+26)%26;

进行推广,如果一个长度为n的数组,假定当前下标为i,我们对其向左进行k次「旋转」,最后的下标为:

(i-k+n)%n

T4.删除操作后的最大子段和

看大佬的解法,是并查集的套路题,然而并查集我只草草做了下,如今连模板也不会默写了,看来又是埋坑并查集专题了,连着前两次周赛的数位dp专题和图的深搜专题再次埋坑。贴下灵神的题解。

第307场周赛

这场周赛整个就是掉分状态,做完第一题直接开始摆烂,第二题感觉可以贪心做,但是又不知道如何使得最终结果是回文串。第三题我先建图,然后尝试用深搜做,但是深搜实在不太熟练,不清楚应该怎么记录搜索次数以及base条件。第四题一眼堆,但我也不会堆,整个就是一个坐牢。

T1直接从前往后模拟就好

T2.最大回文数字

直接贪心做法,将回文数字看成前半部分和前半部分的翻转拼接而成(相对于偶数而言,奇数只需要在中间加一个数字就行),长度越长,前半部分的数字越大,最后的回文数字越大。我们只需要先将1-9有偶数个个数的数字尽可能的构成前半部分(因为有偶数个数,所以后半部分反转数字也可以和他对称),同时考虑到不能包含前导0,因此只有当前半部分长度不为0时,才可以加入0。在把偶数对都用完的情况下,就看是否还可以在中间加一个数字,从9-1里面挑个最大的夹在中间。

全为0的时候要进行特判返回“0”

class Solution {
    public String largestPalindromic(String num) {
        int[]cnt=new int[10];
        int len=num.length();
        for(int i=0;i<len;i++){
            cnt[num.charAt(i)-'0']++;
        }

        //全为0的时候需要进行特判
        if(cnt[0]==len) return "0";

        //构成前半部分
        StringBuilder sb=new StringBuilder();
        for(int i=9;i>=1;i--){
            sb.append(String.valueOf(i).repeat(cnt[i]/2));
        }
        //判断是否可以加入0
        if(sb.length()>0) sb.append(String.valueOf(0).repeat(cnt[0]/2));

        //得到反转的后半部分,但需要new StringBuilder(sb),否则将前半部分也反转了
        StringBuilder sbreverse=new StringBuilder(sb).reverse();
        for(int i=9;i>=0;i--){
            if(cnt[i]%2!=0){
                sb.append(i);
                break;
            }
        }
        sb.append(sbreverse);
        return sb.toString();
    }
}

T3.感染二叉树需要的总时间

做题的时候就想着两次DFS,第一次递归建立无向图,第二次遍历增加圈数,但是觉得自己无法实现每圈都能找出所有节点,其实就采用类似层序遍历的方法,每次将队列中的节点全部弹出,将相邻节点入栈,由于是无向图,而不是从根到叶子节点有方向的树,所以得做好标记不要重复访问,数组标记或者Set标记都可以。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    HashMap<Integer,HashSet<Integer>> map;
    public int amountOfTime(TreeNode root, int start) {
        map=new HashMap<>();
        buildGraph(root);
        HashSet<Integer> vis=new HashSet<>();
        Deque<Integer> que=new ArrayDeque<>();
        que.offer(start);
        vis.add(start);
        int res=-1;
        while(!que.isEmpty()){
            res++;
            int size=que.size();
            for(int i=0;i<size;i++){
                int top=que.poll();
                HashSet<Integer>set=map.get(top);
                if(set==null) continue;
                for(int node:set){
                    //不重复访问
                    if(vis.contains(node)) continue;
                    vis.add(node);
                    que.offer(node);
                }
            }
        }
        return res;
    }


    public void buildGraph(TreeNode root){
        if(root==null) return;
        if(root.left!=null){
            HashSet<Integer> set1=map.getOrDefault(root.val,new HashSet<Integer>());
            HashSet<Integer> set2=map.getOrDefault(root.left.val,new HashSet<Integer>());
            set1.add(root.left.val);
            set2.add(root.val);
            map.put(root.val,set1);
            map.put(root.left.val,set2);
        }
        if(root.right!=null){
            HashSet<Integer> set1=map.getOrDefault(root.val,new HashSet<Integer>());
            HashSet<Integer> set2=map.getOrDefault(root.right.val,new HashSet<Integer>());
            set1.add(root.right.val);
            set2.add(root.val);
            map.put(root.val,set1);
            map.put(root.right.val,set2);
        }
        buildGraph(root.left);
        buildGraph(root.right);
    }
}

T4.找出数组的第K大和

这题一眼就是用堆做,但无奈自己堆相关的题目实在做得少,先把灵神的题解贴下,堆的专题也得安排起来了呀。

你可能感兴趣的:(算法人生,leetcode,算法,职场和发展)