Java实现LeetCode 题号:821 - 830

159 阅读4分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

821. 字符的最短距离

给定一个字符串 S 和一个字符 C。返回一个代表字符串 S 中每个字符到字符串 S 中的字符 C 的最短距离的数组。

示例 1:

输入: S = "loveleetcode", C = 'e' 输出: [3, 2, 1, 0, 1, 0, 0, 1, 2, 2, 1, 0] 说明:

字符串 S 的长度范围为 [1, 10000]。 C 是一个单字符,且保证是字符串 S 里的字符。 S 和 C 中的所有字母均为小写字母。

PS:
    找到字符c的下标
    循环每一个位置,如果循环的位置大于第一个字符C下标,则接着找字符C第二个下标
    相当于左右指针的关系,如果左指针不存在了,那么最近距离就是右指针
    如果右指针不存在了,那么最近距离就是左指针
    如果两个指针都存在,我们就找最近的那个指针
class Solution {
     public int[] shortestToChar(String s, char c) {
        int len = s.length();
        int[] arr = new int[len];
        int lastIdx = -1, nextIdx = s.indexOf(c);

        for(int i=0; i<len; i++){
            if(nextIdx > -1 && i > nextIdx){
                lastIdx = nextIdx;
                nextIdx = s.indexOf(c, i);
            }

            if(nextIdx > -1 && lastIdx > -1){
                arr[i] = Math.min(i-lastIdx, nextIdx-i);
            } else if(lastIdx == -1){
                arr[i] = nextIdx-i;
            } else if(nextIdx == -1){
                arr[i] = i-lastIdx;
            }
        }

        return arr;
    }

}

822. 翻转卡片游戏

在桌子上有 N 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。

我们可以先翻转任意张卡片,然后选择其中一张卡片。

如果选中的那张卡片背面的数字 X 与任意一张卡片的正面的数字都不同,那么这个数字是我们想要的数字。

哪个数是这些想要的数字中最小的数(找到这些数中的最小值)呢?如果没有一个数字符合要求的,输出 0。

其中, fronts[i] 和 backs[i] 分别代表第 i 张卡片的正面和背面的数字。

如果我们通过翻转卡片来交换正面与背面上的数,那么当初在正面的数就变成背面的数,背面的数就变成正面的数。

示例:

输入:fronts = [1,2,4,4,7], backs = [1,3,4,1,3] 输出:2 解释:假设我们翻转第二张卡片,那么在正面的数变成了 [1,3,4,4,7] , 背面的数变成了 [1,2,4,1,3]。 接着我们选择第二张卡片,因为现在该卡片的背面的数是 2,2 与任意卡片上正面的数都不同,所以 2 就是我们想要的数字。

提示:

1 <= fronts.length == backs.length <= 1000 1 <= fronts[i] <= 2000 1 <= backs[i] <= 2000

PS:
	我的正反相等的我一定不能选,
	然后因为是我随意安排初始的正反顺序,我可以直接找最小的,
class Solution {
   public int flipgame(int[] fronts, int[] backs) {
        int[] bothCnt = new int[2001];
        for (int i = 0; i < fronts.length; ++i) {
            if (fronts[i] == backs[i]) {
                bothCnt[fronts[i]]++;
            }
        }
        
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < fronts.length; ++i) {
            int cnt = bothCnt[fronts[i]];
            if (cnt == 0) {
                min = Math.min(min, fronts[i]);
            }
            
            cnt = bothCnt[backs[i]];
            if (cnt == 0) {
                min = Math.min(min, backs[i]);
            }
        }
        if (min == Integer.MAX_VALUE) return 0;
        else return min;
    }
}

823. 带因子的二叉树

给出一个含有不重复整数元素的数组,每个整数均大于 1。

我们用这些整数来构建二叉树,每个整数可以使用任意次数。

其中:每个非叶结点的值应等于它的两个子结点的值的乘积。

满足条件的二叉树一共有多少个?返回的结果应模除 10 ** 9 + 7。

示例 1:

输入: A = [2, 4] 输出: 3 解释: 我们可以得到这些二叉树: [2], [4], [4, 2, 2] 示例 2:

输入: A = [2, 4, 5, 10] 输出: 7 解释: 我们可以得到这些二叉树: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2].

提示:

1 <= A.length <= 1000. 2 <= A[i] <= 10 ^ 9.

PS:
	直接找能%的,并且余数为0,
	从小到大找,有剪枝操作
class Solution {
    public int numFactoredBinaryTrees(int[] A) {
        int size = A.length;
        Arrays.sort(A);
        long[] dp = new long[size];
        long ans = 1;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < size; i++) map.put(A[i], i);

        dp[0] = 1;
        for (int i = 1; i < size; i++) {
            int vi = A[i];
            long curres = 1;
            for (int j = 0; j < i; j++) {
                int vj = A[j];
                if (vj * vj > vi) break;
                Integer nj;
                if (vi % vj == 0 && (nj = map.get(vi/vj)) != null) {
                    curres += dp[j] * dp[nj] * (nj == j ? 1 : 2);
                    curres %= 1000000007;
                }
            }
            ans += (dp[i] = curres);
        }
        return (int)(ans % 1000000007);
    }
 
}

824. 山羊拉丁文

给定一个由空格分割单词的句子 S。每个单词只包含大写或小写字母。

我们要将句子转换为 “Goat Latin”(一种类似于 猪拉丁文 - Pig Latin 的虚构语言)。

山羊拉丁文的规则如下:

如果单词以元音开头(a, e, i, o, u),在单词后添加"ma"。 例如,单词"apple"变为"applema"。

如果单词以辅音字母开头(即非元音字母),移除第一个字符并将它放到末尾,之后再添加"ma"。 例如,单词"goat"变为"oatgma"。

根据单词在句子中的索引,在单词最后添加与索引相同数量的字母'a',索引从1开始。 例如,在第一个单词后添加"a",在第二个单词后添加"aa",以此类推。 返回将 S 转换为山羊拉丁文后的句子。

示例 1:

输入: "I speak Goat Latin" 输出: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa" 示例 2:

输入: "The quick brown fox jumped over the lazy dog" 输出: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa" 说明:

S 中仅包含大小写字母和空格。单词间有且仅有一个空格。 1 <= S.length <= 150。

PS:
    暴力筛选,循环遍历每一个单词,然后判断是否为元音(这里的元音要注意大小写)
    元音+ma
    辅音,去掉第一个字符,然后加ma
        
class Solution {
 public String toGoatLatin(String S) {

        String[] words = S.split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < words.length; i++) {
            sb.append(makeGoatLatin(words[i], i));
            sb.append(' ');
        }

        String ans = sb.toString();
        return ans.substring(0, ans.length() - 1);
    }

    private static String makeGoatLatin(String word, int index) {

        StringBuilder sb = new StringBuilder();
        char ch = word.charAt(0);
        if (ch == 'a' || ch == 'e' || ch == 'i' ||ch == 'o' ||ch == 'u' || ch == 'A' || ch == 'E' || ch == 'I' ||ch == 'O' ||ch == 'U') {
            sb.append(word);
        } else {
            sb.append(word.substring(1));
            sb.append(ch);
        }
        sb.append("ma");

        for (int i = 0; i <= index; i++) {
            sb.append('a');
        }

        return sb.toString();
    }
}

825. 适龄的朋友

人们会互相发送好友请求,现在给定一个包含有他们年龄的数组,ages[i] 表示第 i 个人的年龄。

当满足以下条件时,A 不能给 B(A、B不为同一人)发送好友请求:

age[B] <= 0.5 * age[A] + 7 age[B] > age[A] age[B] > 100 && age[A] < 100 否则,A 可以给 B 发送好友请求。

注意如果 A 向 B 发出了请求,不等于 B 也一定会向 A 发出请求。而且,人们不会给自己发送好友请求。

求总共会发出多少份好友请求?

示例 1:

输入: [16,16] 输出: 2 解释: 二人可以互发好友申请。 示例 2:

输入: [16,17,18] 输出: 2 解释: 好友请求可产生于 17 -> 16, 18 -> 17. 示例 3:

输入: [20,30,100,110,120] 输出: 3 解释: 好友请求可产生于 110 -> 100, 120 -> 110, 120 -> 100.

说明:

1 <= ages.length <= 20000. 1 <= ages[i] <= 120.

PS:
    先分析一波题目,我们可以发现,如果符合条件3,那么一定符合条件2
    使用计数排序方法,把年龄当作下标进行计数
    
    然后相同年龄的如果符合加起来,不同年龄的如果符合加起来
    
class Solution {
    public int numFriendRequests(int[] ages) {
        int[] nums = new int[121];
        for (int age : ages) {
            nums[age] ++;
        }

        int ans = 0;
        for (int i = 120; i >= 1; i --) {
            if (nums[i] == 0) {
                continue;
            }

            if (i > 0.5 * i + 7) {
                ans += nums[i] * (nums[i] - 1);
            }

            for (int j = i - 1; j >= 1; j --) {
                if (j <= 0.5 * i + 7) {
                    break;
                } else {
                    ans += nums[j] * nums[i];
                }
            }
        }

        return ans;
    }
}

826. 安排工作以达到最大收益

有一些工作:difficulty[i] 表示第i个工作的难度,profit[i]表示第i个工作的收益。

现在我们有一些工人。worker[i]是第i个工人的能力,即该工人只能完成难度小于等于worker[i]的工作。

每一个工人都最多只能安排一个工作,但是一个工作可以完成多次。

举个例子,如果3个工人都尝试完成一份报酬为1的同样工作,那么总收益为 3。如果一个工人不能完成任何工作,他的收益为3。如果一个工人不能完成任何工作,他的收益为 0 。

我们能得到的最大收益是多少?

示例:

输入: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker = [4,5,6,7] 输出: 100 解释: 工人被分配的工作难度是 [4,4,6,6] ,分别获得 [20,20,30,30] 的收益。 提示:

1 <= difficulty.length = profit.length <= 10000 1 <= worker.length <= 10000 difficulty[i], profit[i], worker[i] 的范围是 [1, 10^5]

PS:
	把任务度和利益放进DP,
	然后直接取
import java.awt.Point;

class Solution {
    public int maxProfitAssignment(int[] difficulty, int[] profit, int[] worker) {
        int n = difficulty.length;
        int m = worker.length;
        int[] v = new int[100001];
        for(int i=0;i<n;i++){
            if(v[difficulty[i]]<profit[i]){
                v[difficulty[i]] = profit[i];
            }
        }
        int max = 0;
        for(int i=0;i<100001;i++){
            max = Math.max(max,v[i]);
            v[i] = max;
        }

       
        int res = 0;
        for(int num:worker){
            res+=v[num];
        }
        return res;

    }
}

827. 最大人工岛

在二维地图上, 0代表海洋, 1代表陆地,我们最多只能将一格 0 海洋变成 1变成陆地。

进行填海之后,地图上最大的岛屿面积是多少?(上、下、左、右四个方向相连的 1 可形成岛屿)

示例 1:

输入: [[1, 0], [0, 1]] 输出: 3 解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。 示例 2:

输入: [[1, 1], [1, 0]] 输出: 4 解释: 将一格0变成1,岛屿的面积扩大为 4。 示例 3:

输入: [[1, 1], [1, 1]] 输出: 4 解释: 没有0可以让我们变成1,面积依然为 4。 说明:

1 <= grid.length = grid[0].length <= 50 0 <= grid[i][j] <= 1

PS:
	DFS把岛屿拿出来,每个岛屿放上不同的标记
	然后,把每个岛屿的面积放进数组存上,
	然后再找每一个海洋,看看要是能把当前海洋变成陆地,组成的最大的面积是多少记录一下
class Solution {
     public int largestIsland(int[][] grid) {
        int[] map = new int[grid.length*grid[0].length/2+3];
        int index = 2;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == 1) {
                    fullIsland(grid, i, j, index++);
                }
            }
        }
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] > 0) {
                    map[grid[i][j]]++;
                }
            }
        }
        int max = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == 0) {
                    int t = 1,t1=0,t2=0,t3=0,t4=0;
                    if (i > 0 && (t1 = grid[i - 1][j]) > 1) {
                        t += map[t1];
                    }
                    if (i < grid.length - 1 && (t2 = grid[i + 1][j]) > 1 && t2 !=t1) {
                        t += map[t2];
                    }
                    if (j > 0 && (t3 = grid[i][j - 1]) > 1 && t2 !=t3 && t1 !=t3) {
                        t += map[t3];
                    }
                    if (j < grid[0].length - 1 && (t4 = grid[i][j + 1]) > 1 && t4 != t3 && t2 != t4 && t1 != t4) {
                        t += map[t4];
                    }
                    max = Math.max(max, t);
                }
            }
        }
        if (max == 0) {
            if (grid[0][0] == 0) {
                return 1;
            } else {
                return grid.length*grid[0].length;
            }
        }
        return max;
    }

    public void fullIsland(int[][] grid, int i, int j, int index) {
        grid[i][j] = index;
        if (i > 0 && grid[i - 1][j] == 1) {
            fullIsland(grid, i - 1, j, index);
        }
        if (i < grid.length - 1 && grid[i + 1][j] == 1) {
            fullIsland(grid, i + 1, j, index);
        }
        if (j > 0 && grid[i][j - 1] == 1) {
            fullIsland(grid, i, j - 1, index);
        }
        if (j < grid[0].length - 1 && grid[i][j + 1] == 1) {
            fullIsland(grid, i, j + 1, index);
        }
    }
}

828. 统计子串中的唯一字符

我们定义了一个函数 countUniqueChars(s) 来统计字符串 s 中的唯一字符,并返回唯一字符的个数。

例如:s = "LEETCODE" ,则其中 "L", "T","C","O","D" 都是唯一字符,因为它们只出现一次,所以 countUniqueChars(s) = 5 。

本题将会给你一个字符串 s ,我们需要返回 countUniqueChars(t) 的总和,其中 t 是 s 的子字符串。注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计 s 的所有子字符串中的唯一字符)。

由于答案可能非常大,请将结果 mod 10 ^ 9 + 7 后再返回。

示例 1:

输入: "ABC" 输出: 10 解释: 所有可能的子串为:"A","B","C","AB","BC" 和 "ABC"。 其中,每一个子串都由独特字符构成。 所以其长度总和为:1 + 1 + 1 + 2 + 2 + 3 = 10 示例 2:

输入: "ABA" 输出: 8 解释: 除了 countUniqueChars("ABA") = 1 之外,其余与示例 1 相同。 示例 3:

输入:s = "LEETCODE" 输出:92

提示:

0 <= s.length <= 10^4 s 只包含大写英文字符

class Solution {
    /*
    🐂🍺的解释
    对每一个字符i,向前找到相同的字符j,向后找到相同的字符k。当前字符对最终结果的贡献是:(i-j)*(k-i)。

这相当于两种方案的拼接:在字符串A(j到i)当中,字符i贡献的次数是(i-j)次。在字符串B(k-i)当中,字符i贡献的次数是(k-i)。那么当两者拼接的时候,字符i对子串(j到k)的贡献就是两种方案的乘积(符合乘法公式)。
    */
      public int uniqueLetterString(String s) {
        char[] cs = s.toCharArray();
        int size = cs.length;
        int[][] pos = new int[26][2];
        for (int i = 0; i < 26; i++) {
            pos[i][0] = pos[i][1] = -1;
        }
        long ans = 0;

        for (int i = 0; i < size; i++) {
            int c = cs[i] - 'A';
            int[] curpos = pos[c];
            if ( curpos[0] != -1 || curpos[1] != -1 ) {
                ans += (i-curpos[1]) * (curpos[1]-curpos[0]);
            }
            curpos[0] = curpos[1];
            curpos[1] = i;
        }

        for (int i = 0; i < 26; i++) {
            int[] curpos = pos[i];
            if (curpos[0] != -1 || curpos[1] != -1) {
                ans += (size-curpos[1]) * (curpos[1]-curpos[0]);
            }
        }

        return (int)(ans % 1000000007);
    }
 
}

830. 较大分组的位置

在一个由小写字母构成的字符串 S 中,包含由一些连续的相同字符所构成的分组。

例如,在字符串 S = "abbxxxxzyy" 中,就含有 "a", "bb", "xxxx", "z" 和 "yy" 这样的一些分组。

我们称所有包含大于或等于三个连续字符的分组为较大分组。找到每一个较大分组的起始和终止位置。

最终结果按照字典顺序输出。

示例 1:

输入: "abbxxxxzzy" 输出: [[3,6]] 解释: "xxxx" 是一个起始于 3 且终止于 6 的较大分组。 示例 2:

输入: "abc" 输出: [] 解释: "a","b" 和 "c" 均不是符合要求的较大分组。 示例 3:

输入: "abcdddeeeeaabbbcd" 输出: [[3,5],[6,9],[12,14]] 说明: 1 <= S.length <= 1000

PS:
    暴力循环
    字符相同时计数,
    如果相同字符数量大于等于3,那么可以认为是一个较大分组
class Solution {
    public List<List<Integer>> largeGroupPositions(String S) {
        char[] sarr = S.toCharArray();
        List<List<Integer>> lists = new ArrayList<>();

        List<Integer> list = null;

        int count = 1;  // 记录下出现的次数
        for (int i = 1; i < sarr.length; i++) {
            if (sarr[i] == sarr[i - 1]) {
                count ++;
            } else {
                if (count >= 3) {
                    list = new ArrayList<>();
                    list.add(i - count);
                    list.add(i - 1);
                    lists.add(list);
                }
                count = 1;
            }
            if (count >= 3 && i == sarr.length - 1) {
                list = new ArrayList<>();
                list.add(i - count + 1);
                list.add(i);
                lists.add(list);
            }
        }
        return lists;
    }
}