LeetCode刷题笔记(Java)---第521-540题

前言

需要开通vip的题目暂时跳过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

521. 最长特殊序列 Ⅰ

给你两个字符串,请你从这两个字符串中找出最长的特殊序列。

「最长特殊序列」定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。

子序列 可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。

输入为两个字符串,输出最长特殊序列的长度。如果不存在,则返回 -1。

在这里插入图片描述

  • 解答

    public int findLUSlength(String a, String b) {
          
          
            if(a.equals(b))return -1;
            int lenA = a.length();
            int lenB = b.length();
            return lenA >= lenB ? lenA : lenB;
        }
    
  • 分析

    1. 首先判断两个字符串是否相同,相同则返回-1
    2. 然后判断两个字符串的长度。
    3. 返回较长的,就是最长的特殊序列,等长的话任意返回。
  • 提交结果在这里插入图片描述

522. 最长特殊序列 II

给定字符串列表,你需要从它们中找出最长的特殊序列。最长特殊序列定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。

子序列可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。

输入将是一个字符串列表,输出是最长特殊序列的长度。如果最长特殊序列不存在,返回 -1 。
在这里插入图片描述

  • 解答

    public int findLUSlength(String[] strs) {
          
          
            Set<String> set = new HashSet<>();
            Set<String> rep = new HashSet<>();
            for(String str:strs){
          
          
                if(set.contains(str)){
          
          
                    rep.add(str);
                }else{
          
          
                    set.add(str);
                }
            }
            set.removeAll(rep);
            int res = -1;
            for(String str: set){
          
          
                if(str.length()>res){
          
          
                    if(isOk(str,set) && isOk(str,rep))
                        res = str.length();
                }
            }
            return res;
        }
    
        public boolean isOk(String str,Set<String> set){
          
          
            for(String s:set){
          
          
                if(s.equals(str))continue;
                if(s.length() < str.length())continue;
                int strIndex = 0;
                int sIndex = 0;
                while(sIndex < s.length() && strIndex < str.length()){
          
          
                    if(str.charAt(strIndex) == s.charAt(sIndex)){
          
          
                        strIndex++;
                        sIndex++;
                    }else{
          
          
                        sIndex++;
                    }
                }
                if(strIndex == str.length())return false;
            }
            return true;
        }
    
  • 分析

    1. 去重,重复的字符串放入rep集合当中,其余的在set集合当中。
    2. 遍历set集合,若当前字符串的长度大于已经记录的最大长度
    3. 继续判断当前字符串是否是其余字符串的子串,若都不是则返回true,更新最大长度为当前字符串的长度。
  • 提交结果在这里插入图片描述

523. 连续的子数组和

给定一个包含 非负数 的数组和一个目标 整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。

在这里插入图片描述

  • 解答

    public boolean checkSubarraySum(int[] nums, int k) {
          
          
            int[] preSum = new int[nums.length];
            preSum[0] = nums[0];
            for(int i = 1;i < nums.length;i++){
          
          
                preSum[i] = preSum[i-1] + nums[i];
                if(preSum[i] == 0)return true;
                if(k != 0 && preSum[i] % k == 0)return true;
                for(int j = 0;i - j >1 ;j++){
          
          
                    if(preSum[i] - preSum[j] == 0 || (k != 0 && (preSum[i] - preSum[j]) % k == 0))return true;
                }
            }
            return false;
        }
    
  • 分析

    1. 记录前缀和
    2. 然后暴力的求解子数组的区间和,判断是否是k的倍数。
    3. 注意k=0的情况。
  • 提交结果在这里插入图片描述

524. 通过删除字母匹配到字典里最长单词

给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
在这里插入图片描述

  • 解答

    public String findLongestWord(String s, List<String> d) {
          
          
            Collections.sort(d, new Comparator<String>() {
          
          
                @Override
                public int compare(String o1, String o2) {
          
          
                    if(o2.length() != o1.length())
                        return o2.length() - o1.length();
                    for(int i = 0;i < o1.length();i++){
          
          
                        if(o1.charAt(i) != o2.charAt(i)){
          
          
                            return o1.charAt(i) - o2.charAt(i);
                        }
                    }
                    return 0;
                }
            });
            int[] charNumbers = new int[26];
            for(int i = 0;i < s.length();i++){
          
          
                charNumbers[s.charAt(i) - 'a']++;
            }
            for(String str:d){
          
          
                if(!isOk(charNumbers,str))continue;
                int strIndex = 0;
                int sIndex = 0;
                while(strIndex < str.length() && sIndex < s.length()){
          
          
                    if(str.charAt(strIndex) == s.charAt(sIndex)){
          
          
                        strIndex++;
                        sIndex++;
                    }else sIndex++;
                }
                if(strIndex == str.length())return str;
            }
            return "";
        }
    
        public boolean isOk(int[] charNumbers,String str){
          
          
            int[] strNumbrs = new int[26];
            for(int i = 0;i < str.length();i++){
          
          
                char cur = str.charAt(i);
                strNumbrs[cur - 'a']++;
                if(strNumbrs[cur - 'a'] > charNumbers[cur - 'a'])return false;
            }
            return true;
        }
    
  • 分析

    1. 对列表d进行排序,长度不想等的时候,长度长的排前面。若长度相等,则字典序小的排前面。
    2. 用数组存储s字符串中字符出现的次数。
    3. 遍历列表d中的字符串。
    4. 首先判断当前的字符串中各个字符出现的次数是否超过了s字符串中出现的次数,若超过了则跳过这个字符串。
    5. 若没有超过,则继续判断是否满足字母间的相对顺序。
    6. 两个指针同时遍历str和s。若遍历的结果 strIndex 等于 str.length() 说明可以通过字符串s 来得到str
  • 提交结果在这里插入图片描述

525. 连续数组

给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度)。
在这里插入图片描述

  • 解答

    //方法1 超出内存限制
    public int findMaxLength(int[] nums) {
          
          
            int len = nums.length;
            int[][] dp = new int[len][len];
            for(int i = 0;i < len;i++){
          
          
                if(nums[i] == 0){
          
          
                    dp[i][i] = -1;
                }else{
          
          
                    dp[i][i] = 1;
                }
            }
            int res = 0;
            for(int l = 2;l <= len;l++){
          
          
                for(int i = 0;i < len - 1 && i + l - 1 < len;i++){
          
          
                    int curNumIndex = i + l - 1;
                    if(nums[curNumIndex] == 1){
          
          
                        dp[i][i+l-1] = dp[i][i+l-2] + 1;
                    }else{
          
          
                        dp[i][i+l-1] = dp[i][i+l-2] - 1;
                    }
                    if(dp[i][i+l-1] == 0){
          
          
                        res = Math.max(res,l);
                    }
                }
            }
            return res;
        }
    // 方法2 超出时间限制
    public int findMaxLength(int[] nums) {
          
          
            int len = nums.length;
            int[] dp = new int[len];
            for(int i = 0;i < len;i++){
          
          
                if(nums[i] == 0){
          
          
                    dp[i] = -1; 
                }else{
          
          
                    dp[i] = 1;
                }
            }
            int res = 0;
            for(int l = 2;l <= len;l++){
          
          
                for(int i = len - 1;i -l + 1 >= 0;i--){
          
          
                    int curIndex = i - l + 1;
                    if(nums[curIndex] == 1){
          
          
                        dp[i] = dp[i] + 1;
                    }else{
          
          
                        dp[i] = dp[i] - 1;
                    }
                    if(dp[i] == 0){
          
          
                        res = Math.max(res,l);
                    }
                }
            }
            return res;
        }
    //方法3
    public int findMaxLength(int[] nums) {
          
          
            int[] arr = new int[2 * nums.length + 1];
            Arrays.fill(arr, -2);
            arr[nums.length] = -1;
            int maxlen = 0, count = 0;
            for (int i = 0; i < nums.length; i++) {
          
          
                count = count + (nums[i] == 0 ? -1 : 1);
                if (arr[count + nums.length] >= -1) {
          
          
                    maxlen = Math.max(maxlen, i - arr[count + nums.length]);
                } else {
          
          
                    arr[count + nums.length] = i;
                }
    
            }
            return maxlen;
        }
    
  • 分析

    1. 一开始使用dp,超内存空间,

    2. 于是状态压缩 改成第二版本的dp,超时 说明只能用O(n)的复杂度

    3. 方法3,官方题解 记录数字o和1出现的相对次数

    4. 比如遇到0的话 count - 1,遇到1的话 count + 1

    5. 若某一时刻 count变成了0,这表示从开头到当前位置遇到的0和1的数目一样多,另外 当遇到两次相同的count的时候,这意味着这两个位置之间的0和1的数目一样多。在这里插入图片描述

      (A,B), (B,C) 和 (A,C) 所代表的子数组含有相同数目的 0 和 1 。

    6. 数组长度为n,那么可能出现的count的情况 就是 2 * n + 1.也就是 [-n,n]的区间。

    7. 所以需要构建一个数组arr,来存储所有可能的count的状态。也就是记录当前count状态下的数组索引的位置。

    8. 初始化count的状态为-2

    9. 初始化arr[nums.length] = -1 是为了找到count为0的情况下,开头到遍历位置的长度。

    10. 遍历数组nums

    11. 计算相对值count

    12. 若arr[count + nums.length] >= -1 说明之前以及出现过count的值。

    13. 那么就计算当前位置 i 和上一次出现count值的位置之间的距离。和maxLen比较,保留较大者

    14. 若当前count还没有出现过,那么就记录下这个count状态下,的索引位置。

    15. 一次遍历,即可找到最大值。

    16. 题解的另一个解法是使用hashMap代替arr,key是count,value是索引位置,道理是一样的。

  • 提交结果在这里插入图片描述

526. 优美的排列

假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:

  1. 第 i 位的数字能被 i 整除
  2. i 能被第 i 位上的数字整除

现在给定一个整数 N,请问可以构造多少个优美的排列?

在这里插入图片描述

  • 分析

    1. 回溯递归
    2. 遍历数组,若当前位置还没有数字占有并且满足条件那么占有此位置,递归。
    3. 若能够遍历完找到最后一个数字放入的位置,那么res+1.
  • 提交结果在这里插入图片描述

528. 按权重随机选择

给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。

例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。

也就是说,选取下标 i 的概率为 w[i] / sum(w) 。

在这里插入图片描述

  • 解答

    class Solution {
          
          
        int[] wsum;
        Random rand = new Random();
        public Solution(int[] w) {
          
          
            wsum = w;
            for(int i = 1; i < wsum.length; i++){
          
          
                wsum[i] += wsum[i - 1];
            }
        }
        
        public int pickIndex() {
          
          
            int target = this.rand.nextInt(wsum[wsum.length - 1]) + 1;
            int start = 0;
            int end = wsum.length - 1;
            while(start + 1 < end){
          
          
                int mid = start + ((end - start) >> 1);
                if(wsum[mid] <= target){
          
          
                    start = mid;
                }else{
          
          
                    end = mid;
                }
            }
            if(wsum[start] >= target){
          
          
                return start;
            }
            return end;
        }
    }
    
  • 分析

    1. 计算前缀和,然后再总和区间的范围内生成随机数。

    2. 利用二分查找寻找第一个比生成的随机数大的数字。返回它的索引

  • 提交结果在这里插入图片描述

529. 扫雷游戏

让我们一起来玩扫雷游戏!

给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。

现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:

  1. 如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
  2. 如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
  3. 如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
  4. 如果在此次点击中,若无更多方块可被揭露,则返回面板。
  • 解答

    public char[][] updateBoard(char[][] board, int[] click) {
          
          
            if(board[click[0]][click[1]] == 'M'){
          
          
                board[click[0]][click[1]] = 'X';
                return board;
            }
            dfs(board,click[0],click[1]);
            return board;
        }
        public void dfs(char[][] board,int rowIndex,int colIndex){
          
          
            if(!(rowIndex >=0 && rowIndex < board.length && colIndex >=0 && colIndex < board[0].length))return;
            if(board[rowIndex][colIndex] == 'E'){
          
          
                int number = 0;
                for(int i = rowIndex - 1;i <= rowIndex + 1;i++){
          
          
                    for(int j = colIndex - 1; j <= colIndex + 1;j++){
          
          
                        if( i>=0 && i < board.length && j >= 0 && j < board[0].length && board[i][j] == 'M')
                            number++;
                    }
                }
                if(number > 0)board[rowIndex][colIndex] = (char)(number + '0');
                else {
          
          
                    board[rowIndex][colIndex] = 'B';
                    dfs(board,rowIndex-1,colIndex-1);
                    dfs(board,rowIndex-1,colIndex);
                    dfs(board,rowIndex-1,colIndex+1);
                    dfs(board,rowIndex,colIndex-1);
                    dfs(board,rowIndex,colIndex+1);
                    dfs(board,rowIndex+1,colIndex-1);
                    dfs(board,rowIndex+1,colIndex);
                    dfs(board,rowIndex+1,colIndex+1);
                }
            }
        }
    
  • 分析

    1. 首先一开始点击的位置,判断是否是M 如果是的话将它修改为X 直接返回。
    2. 然后判断当前位置是否是E
    3. 如果是E的话,则计算它周围九宫格内 M的数量。
    4. 若M的数量大于0,那么将该位置设置为这个数量
    5. 否则设置为B,然后递归的去判断周围九宫格内的其他格子的情况。
    6. 递归进行。
  • 提交结果在这里插入图片描述

532. 数组中的 k-diff 数对

给定一个整数数组和一个整数 k,你需要在数组里找到不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。

这里将 k-diff 数对定义为一个整数对 (nums[i], nums[j]),并满足下述全部条件:

  • 0 <= i, j < nums.length
  • i != j
  • |nums[i] - nums[j]| == k

注意,|val| 表示 val 的绝对值。

在这里插入图片描述

  • 解答

    public int findPairs(int[] nums, int k) {
          
          
            Arrays.sort(nums);
            Map<Integer,Integer> map = new HashMap<>();
            Set<Integer> set = new HashSet<>();
            for(int i = 0;i < nums.length;i++){
          
          
                int curNum = nums[i];
                if(map.containsKey(curNum)){
          
          
                    int oNum = map.get(curNum);
                    int temp = oNum;
                    if(curNum < oNum){
          
          
                        temp = curNum;
                    }
                    set.add(temp);
                }
                map.put(curNum - k,curNum);
                map.put(curNum + k,curNum);
            }
            return set.size();
        }
    
  • 分析

    1. 先对数组排序

    2. map集合用来存储可以组合成k的两个值。

    3. key是要寻找的目标,value是已经遍历过的数字。

    4. set集合用来去重,保留两个数字组合中较小者

    5. 遍历数组

    6. 若当前的数字存在map当中,则将map当中记录的另一个数字取出,比较大小,较小者存入set当中。

    7. map放入 当前数字减k 作为key ,curNum作为value

      当然数字加k作为key,curNum作为value

    8. 最后返回set的大小即可。

  • 提交结果在这里插入图片描述

537. 复数乘法

给定两个表示复数的字符串。

返回表示它们乘积的字符串。注意,根据定义 i2 = -1 。

在这里插入图片描述

  • 解答

    public String complexNumberMultiply(String a, String b) {
          
          
            String[] aStr = a.split("\\+");
            String[] bStr = b.split("\\+");
            int number1 = Integer.valueOf(aStr[0]);
            int number2 = Integer.valueOf(bStr[0]);
            int fu1 = Integer.valueOf(aStr[1].substring(0,aStr[1].length()-1));
            int fu2 = Integer.valueOf(bStr[1].substring(0,bStr[1].length()-1));
            int res1 = number1 * number2;
            int res2 = fu1 * fu2 * -1;
            int res3 = number1 * fu2 + number2 * fu1;
            String res = res1 +res2 + "+" + res3 + "i";
            return res;
        }
    
  • 分析

    1. 根据加号区分实数部分和虚数部分
    2. 然后根据复数的乘法公式计算结果即可。
  • 提交结果在这里插入图片描述

539. 最小时间差

给定一个 24 小时制(小时:分钟 “HH:MM”)的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。

在这里插入图片描述

  • 解答

    public int findMinDifference(List<String> timePoints) {
          
          
            PriorityQueue<Integer> queue = new PriorityQueue<>();
            for(String timePoint:timePoints){
          
          
                String[] strs1 = timePoint.split(":");
                int h1 = Integer.valueOf(strs1[0]);
                int m1 = Integer.valueOf(strs1[1]);
                if(h1 == 0 && m1 == 0)h1 = 24;
                queue.add(h1 * 60 + m1);
            }
            int start = queue.poll();
            int last = start;
            int end= 0;
            int res = Integer.MAX_VALUE;
            while(!queue.isEmpty()){
          
          
                int cur = queue.poll();
                res = Math.min(cur - last,res);
                last = cur;
            }
            end = last;
            int endSubStart = 24 * 60 - end + start;
            res = Math.min(res,endSubStart);
            return res;
        }
    
  • 分析

    1. 使用优先级队列排序
    2. 将时钟转换成分钟数,插入到优先级队列当中。
    3. 依次从队中取出数字,相邻两个数字之间的差值的最小值记录在res当中。
    4. 并且还需要计算队中第一个数字和最后一个数字的差值。
  • 提交结果在这里插入图片描述

540. 有序数组中的单一元素

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

在这里插入图片描述

  • 解答

    public int singleNonDuplicate(int[] nums) {
          
          
            int res = 0;
            for(int i = 0;i < nums.length;i++){
          
          
                res ^=nums[i];
            }
            return res;
        }
    
  • 分析

    1. 两个相同的数字异或得0
    2. 0和任意数字异或得本身
    3. 所以只需要将所有的数字进行异或即可。
  • 提交结果在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/110792352