文章目录
前言
需要开通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
- 然后判断两个字符串的长度。
- 返回较长的,就是最长的特殊序列,等长的话任意返回。
-
提交结果
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; }
-
分析
- 去重,重复的字符串放入rep集合当中,其余的在set集合当中。
- 遍历set集合,若当前字符串的长度大于已经记录的最大长度
- 继续判断当前字符串是否是其余字符串的子串,若都不是则返回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; }
-
分析
- 记录前缀和
- 然后暴力的求解子数组的区间和,判断是否是k的倍数。
- 注意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; }
-
分析
- 对列表d进行排序,长度不想等的时候,长度长的排前面。若长度相等,则字典序小的排前面。
- 用数组存储s字符串中字符出现的次数。
- 遍历列表d中的字符串。
- 首先判断当前的字符串中各个字符出现的次数是否超过了s字符串中出现的次数,若超过了则跳过这个字符串。
- 若没有超过,则继续判断是否满足字母间的相对顺序。
- 两个指针同时遍历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; }
-
分析
-
一开始使用dp,超内存空间,
-
于是状态压缩 改成第二版本的dp,超时 说明只能用O(n)的复杂度
-
方法3,官方题解 记录数字o和1出现的相对次数
-
比如遇到0的话 count - 1,遇到1的话 count + 1
-
若某一时刻 count变成了0,这表示从开头到当前位置遇到的0和1的数目一样多,另外 当遇到两次相同的count的时候,这意味着这两个位置之间的0和1的数目一样多。
(A,B), (B,C) 和 (A,C) 所代表的子数组含有相同数目的 0 和 1 。
-
数组长度为n,那么可能出现的count的情况 就是 2 * n + 1.也就是 [-n,n]的区间。
-
所以需要构建一个数组arr,来存储所有可能的count的状态。也就是记录当前count状态下的数组索引的位置。
-
初始化count的状态为-2
-
初始化arr[nums.length] = -1 是为了找到count为0的情况下,开头到遍历位置的长度。
-
遍历数组nums
-
计算相对值count
-
若arr[count + nums.length] >= -1 说明之前以及出现过count的值。
-
那么就计算当前位置 i 和上一次出现count值的位置之间的距离。和maxLen比较,保留较大者
-
若当前count还没有出现过,那么就记录下这个count状态下,的索引位置。
-
一次遍历,即可找到最大值。
-
题解的另一个解法是使用hashMap代替arr,key是count,value是索引位置,道理是一样的。
-
-
提交结果
526. 优美的排列
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
- 第 i 位的数字能被 i 整除
- i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
-
分析
- 回溯递归
- 遍历数组,若当前位置还没有数字占有并且满足条件那么占有此位置,递归。
- 若能够遍历完找到最后一个数字放入的位置,那么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; } }
-
分析
-
计算前缀和,然后再总和区间的范围内生成随机数。
-
利用二分查找寻找第一个比生成的随机数大的数字。返回它的索引
-
-
提交结果
529. 扫雷游戏
让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
- 如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
- 如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
- 如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
- 如果在此次点击中,若无更多方块可被揭露,则返回面板。
-
解答
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); } } }
-
分析
- 首先一开始点击的位置,判断是否是M 如果是的话将它修改为X 直接返回。
- 然后判断当前位置是否是E
- 如果是E的话,则计算它周围九宫格内 M的数量。
- 若M的数量大于0,那么将该位置设置为这个数量
- 否则设置为B,然后递归的去判断周围九宫格内的其他格子的情况。
- 递归进行。
-
提交结果
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(); }
-
分析
-
先对数组排序
-
map集合用来存储可以组合成k的两个值。
-
key是要寻找的目标,value是已经遍历过的数字。
-
set集合用来去重,保留两个数字组合中较小者
-
遍历数组
-
若当前的数字存在map当中,则将map当中记录的另一个数字取出,比较大小,较小者存入set当中。
-
map放入 当前数字减k 作为key ,curNum作为value
当然数字加k作为key,curNum作为value
-
最后返回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; }
-
分析
- 根据加号区分实数部分和虚数部分
- 然后根据复数的乘法公式计算结果即可。
-
提交结果
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; }
-
分析
- 使用优先级队列排序
- 将时钟转换成分钟数,插入到优先级队列当中。
- 依次从队中取出数字,相邻两个数字之间的差值的最小值记录在res当中。
- 并且还需要计算队中第一个数字和最后一个数字的差值。
-
提交结果
540. 有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
-
解答
public int singleNonDuplicate(int[] nums) { int res = 0; for(int i = 0;i < nums.length;i++){ res ^=nums[i]; } return res; }
-
分析
- 两个相同的数字异或得0
- 0和任意数字异或得本身
- 所以只需要将所有的数字进行异或即可。
-
提交结果