zoukankan      html  css  js  c++  java
  • [Leetcode Weekly Contest]258

    链接:LeetCode

    [Leetcode]2000. 反转单词前缀

    给你一个下标从 0 开始的字符串 word 和一个字符 ch 。找出 ch 第一次出现的下标 i ,反转 word 中从下标 0 开始、直到下标 i 结束(含下标 i )的那段字符。如果 word 中不存在字符 ch ,则无需进行任何操作。
    例如,如果 word = "abcdefd" 且 ch = "d" ,那么你应该 反转 从下标 0 开始、直到下标 3 结束(含下标 3 )。结果字符串将会是 "dcbaefd" 。
    返回 结果字符串 。

    暴力即可。

    class Solution {
        public String reversePrefix(String word, char ch) {
            for (int i=0; i<word.length(); i++) {
                if (word.charAt(i) == ch) return reverse(word.substring(0, i+1)) + word.substring(i+1, word.length());
            }
            return word;
        }
    
        private String reverse(String word) {
            char[] arr = word.toCharArray();
            int left = 0, right = arr.length-1;
            while (left < right) {
                char temp = arr[left];
                arr[left] = arr[right];
                arr[right] = temp;
                left++; right--;
            }
            return String.valueOf(arr);
        }
    }
    

    [Leetcode]2001. 可互换矩形的组数

    用一个下标从 0 开始的二维整数数组 rectangles 来表示 n 个矩形,其中 rectangles[i] = [widthi, heighti] 表示第 i 个矩形的宽度和高度。
    如果两个矩形 i 和 j(i < j)的宽高比相同,则认为这两个矩形 可互换 。更规范的说法是,两个矩形满足 widthi/heighti == widthj/heightj(使用实数除法而非整数除法),则认为这两个矩形 可互换 。
    计算并返回 rectangles 中有多少对 可互换 矩形。

    宽高比,我们可以直接算出来。但是直接用内建的高精度小数的话,很可能会被卡精度。
    所以我们把宽高比化成有理数,即宽和高都除以他们的最大公约数。然后用一个hashmap去算同样宽高比的矩形的数量,再之后就转化成一个组合问题啦。
    即宽高比相同的矩形有N个,那么他们组成多少个可互换对呢?
    很显然,答案是 N*(N-1)/2

    class Solution {
        private int gcd(int a, int b) {
            return b == 0 ? a : gcd(b, a % b);
        }
    
        public long interchangeableRectangles(int[][] arr) {
            Map<Long, Long> map = new HashMap<Long, Long>();
            final long BASE = (long)1e8;
    
            for (var p : arr) {
                int a = p[0], b = p[1];
                int div = gcd(a, b);
                // String frac = (a / div) + "/" + (b / div);
                // 使用数字压缩 [x, y] 比较快
                long frac = (a / div) * BASE + (b / div);
                map.put(frac, map.getOrDefault(frac, 0L) + 1);
            }
    
            long res = 0L;
            for (var x : map.values()) {
                res += x * (x - 1) / 2;
            }
            return res;
        }
    }
    
    

    [Leetcode]2002. 两个回文子序列长度的最大乘积

    给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。

    请你返回两个回文子序列长度可以达到的 最大乘积 。

    子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。

    看到2 <= s.length <= 12,就应该能想到状态压缩,通过状态压缩我们可以拿到所有的子序列,然后就判断乘积最大的最长子序列即可。总的来说,还是暴力求解,另外加了一些位运算。

    class Solution {
        public int maxProduct(String s) {
            var total = getTotal(s);
            int n = total.size();
            int res = 1, leng = s.length();
            for(int i=0;i<n-1;++i){
                for(int j=i+1;j<n;++j){
                    int num1 = (int)total.get(i), num2=(int)total.get(j);
                    if(check(num1,num2,leng)){
                        res = Math.max(res,getNum(num1,leng)*getNum(num2,leng));
                    }
                }
            }
            return res;
        }
    
        public ArrayList getTotal(String s){
            int n = s.length();
            int m = 1<<n;
            ArrayList total = new ArrayList<Integer>();
            for(int i=1;i<m;++i)
            {
                String tmp = "";
                for(int ind=0;ind<n;++ind){
                    if((i&(1<<ind))!=0){
                        tmp += s.charAt(ind);
                    }
                }
                if(getReverse(tmp)){
                    total.add(i);
                }
            }
            return total;
        }
    
        public int getNum(int num,int n) {
            int res = 0;
            for(int ind=0;ind<n;++ind) {
                if((num&(1<<ind))!=0){
                    res ++;
                }
            }
            return res;
        }
    
        public boolean check(int num1, int num2, int n) {
            for (int i=0;i<n;++i){
                if((num1&(1<<i))!=0 && (num2&(1<<i))!=0){
                    return false;
                }
            }
            return true;
        }
    
        public boolean getReverse(String s){
            int i=0,j=s.length()-1;
            while(i<=j)
            {
                if(s.charAt(i)!=s.charAt(j)){
                    return false;
                }
                i++;
                j--;
            }
            return true;
        }
    }
    

    [Leetcode]2003. 每棵子树内缺失的最小基因值

    有一棵根节点为 0 的 家族树 ,总共包含 n 个节点,节点编号为 0 到 n - 1 。给你一个下标从 0 开始的整数数组 parents ,其中 parents[i] 是节点 i 的父节点。由于节点 0 是 根 ,所以 parents[0] == -1 。

    总共有 105 个基因值,每个基因值都用 闭区间 [1, 105] 中的一个整数表示。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是节点 i 的基因值,且基因值 互不相同 。
    请你返回一个数组 ans ,长度为 n ,其中 ans[i] 是以节点 i 为根的子树内 缺失 的 最小 基因值。
    节点 x 为根的 子树 包含节点 x 和它所有的 后代 节点。

    如果一棵子树里没有 1,显然这个子树的答案为 1。通过一次 dfs 找出子树内有没有 1 即可完成这一步。
    注意到树上的基因值互不相同,因此有 1 的子树的根形成一条从节点 0 出发,到基因值为 1 的节点的链。接下来计算这条链上的答案即可.
    显然链上以深度较浅的节点为根的子树,完全包含以深度较深的节点为根的子树。因此链上的节点的答案从深到浅一定是不降(non-decreasing)的。因此我们再一次通过 dfs,从深到浅遍历链上的节点,并维护以下变量:
    变量 now:表示链上算出答案的节点中,答案最大是多少。
    数组 vis:表示完成遍历的子树中出现过哪些数。
    遍历节点 uu 时,首先递归计算同在链上的子节点 vv 的答案,然后遍历不在链上的子节点 ww,并将以 ww 为根的子树中的所有数加入 vis。最后不断递增 now 直到找到第一个 vis[now] 为 false 的数,即为 uu 的答案。
    两次 dfs 复杂度均为 (mathcal{O}(n)),now 的值只增不降且最大为 (n + 1),因此整体复杂度(mathcal{O}(n))

    class Solution {
        int n;
        int[] parent;
        int[] num;
        boolean[] exist;
        int[] ans;
        int p = 1;
        Map<Integer, List<Integer>> edges;
        public int[] smallestMissingValueSubtree(int[] parents, int[] nums) {
            n = nums.length;
            parent = parents;
            num = nums;
            exist = new boolean[n + 2];
            ans = new int[n];
            edges = new HashMap<>();
            for (int i = 0; i < n; i++) {
                edges.putIfAbsent(parents[i], new ArrayList<>());
                edges.get(parents[i]).add(i);
            }
            // 标记完后,计算出不含1的子树的答案都是1,此时含1的子树答案还是0
            dfsAbout1(0);
            // 计算含1的子树的答案是多少
            dfsAns(0);
            return ans;
        }
        boolean dfsAbout1(int root) {
            List<Integer> children = edges.get(root);
            boolean res = false;
            if (children != null) {
                for (int child : children) {
                    res |= dfsAbout1(child);
                }
            }
            if (num[root] == 1) res = true;
            if (!res) {
                ans[root] = 1;
            }
            return res;
        }
        void dfsAns(int root) {
            List<Integer> children = edges.get(root);
            if (children != null) {
                // 先递归处理含1的子树
                for (int child : children) {
                    if (ans[child] != 1) {
                        dfsAns(child);
                    }
                }
                // 处理不含1的子树,记录当前子树内含有哪些整数
                for (int child : children) {
                    if (ans[child] == 1) {
                        dfsAdd(child);
                    }
                }
            }
            // 记录当前子树根节点的整数
            exist[num[root]] = true;
            // 查找当前缺失的第一个整数
            while (exist[p]) {
                p++;
            }
            ans[root] = p;
        }
        void dfsAdd(int root) {
            exist[num[root]] = true;
            List<Integer> children = edges.get(root);
            if (children != null) {
                for (int child : children) {
                    dfsAdd(child);
                }
            }
        }
    }
    

    Leetcode

  • 相关阅读:
    JS 面向对象
    inline-block元素间隙处理
    MUI
    MUI
    chrome://inspect调试html页面空白,DOM无法加载的解决方案
    MUI
    MUI
    MUI
    MUI
    MUI
  • 原文地址:https://www.cnblogs.com/hellojamest/p/15291313.html
Copyright © 2011-2022 走看看