【刷题笔记】第四天

2924. 找到冠军 II

题目描述

一场比赛中共有 n 支队伍,按从 0n - 1 编号。每支队伍也是 有向无环图(DAG) 上的一个节点。

给你一个整数 n 和一个下标从 0 开始、长度为 m 的二维整数数组 edges 表示这个有向无环图,其中 edges[i] = [ui, vi] 表示图中存在一条从 ui 队到 vi 队的有向边。

a 队到 b 队的有向边意味着 a 队比 b ,也就是 b 队比 a

在这场比赛中,如果不存在某支强于 a 队的队伍,则认为 a 队将会是 冠军

如果这场比赛存在 唯一 一个冠军,则返回将会成为冠军的队伍。否则,返回 -1

思路分析

思路1:

对于u->v这条边,v肯定不是冠军

所以我们遍历所有的边,将那些v标记上,然后判断1到n的每一个节点,如果该节点为标记,则该节点可能为冠军。

注意:冠军只能有一个,如果有多个节点未被标记,说明找不到冠军,就返回-1。

class Solution {
    public int findChampion(int n, int[][] edges) {
        int ans = -1;
        boolean[] loser = new boolean[n]; // u->v, v一定不是冠军
        for (int[] e : edges) {
            loser[e[1]] = true;
        }
        for (int i = 0; i < n; ++i) {
            if (loser[i]) continue;
            if (ans == -1) {
                ans = i;
            } else { // 存在多个冠军
                return -1;
            }
        }
        return ans;
    }
}

思路2:

入度不为0的节点肯定不是冠军

所有我们先统计所有节点的入度,入度为0的节点可能是冠军

由于冠军只能有一个,所以如果有多个入度为0的节点,则返回-1.

class Solution {
    public int findChampion(int n, int[][] edges) {
        int[] in = new int[n];
        for (int[] e : edges) {
            in[e[1]]++;
        }
        int ans = -1;
        for (int i = 0; i < n; ++i) {
            if (in[i] > 0) continue;
            if (ans == -1) {
                ans = i;
            } else { 
                return -1;
            }
        }
        return ans;
    }
}

1702. 修改后的最大二进制字符串

题目描述

给你一个二进制字符串 binary ,它仅有 0 或者 1 组成。你可以使用下面的操作任意次对它进行修改:

  • 操作 1 :如果二进制串包含子字符串 "00",你可以用 "10" 将其替换。
    • 比方说, “00010” -> “10010”
  • 操作 2 :如果二进制串包含子字符串 "10" ,你可以用 "01" 将其替换。
    • 比方说, “00010” -> “00001

请你返回执行上述操作任意次以后能得到的 最大二进制字符串 。如果二进制字符串 x 对应的十进制数字大于二进制字符串 y 对应的十进制数字,那么我们称二进制字符串 x 大于二进制字符串 y

思路分析

贪心

结论一:最终结果不存在两个相邻的0,因为00通过操作1,可以变为10,比00更大。

结论二:最终结果至多只有一个0。
假设存在多个0,并且多个0不相邻,例如字符串101011,对于2 3 位置的10,通过操作2,可以变为01,所以整体字符串变为100111,再通过操作1,00变为10,所以整体字符串变为110111,这就是最终结果。
观察101011100111,相当于将第二个0,移动到第一个0后面,1依次后移。
再观察100111110111,除最后一个0外,其他位置的0变为1。

结论三:全为1的字符串直接返回

所以总结一下最终结果的构成:

假设第一个0出现的位置为i,i位置后面1的个数为cnt个,那么最终结果的后cnt位肯定都是1,第i位置为0,第i位置其他的元素也全为1.

具体做法:统计第一个0后面1的个数,假设是one个,字符串的长度为n,那么最终结果= n - one - 1个1 + 0 + cnt个1

class Solution {
    public String maximumBinaryString(String binary) {
        int n = binary.length();
        int one = 0; // 记录第一个0后面的1的个数
        boolean flag = false; // 标记第一个0是否出现
        for (int i = 0; i < binary.length(); ++i) {
            if (flag && binary.charAt(i) == '1') {
                one++;
            }
            if (binary.charAt(i) == '0') {
                flag = true;
            }
        }

        if (!flag && one == 0) {
            // 如果全为1(0没有出现),直接返回binary
            return binary;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n - one - 1; ++i) {
            sb.append("1");
        }
        sb.append("0");
        for (int i = 0; i < one; ++i) {
            sb.append("1");
        }
        return sb.toString();
    }
}

830. 较大分组的位置

题目描述

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

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

分组可以用区间 [start, end] 表示,其中 startend 分别表示该分组的起始和终止位置的下标。上例中的 "xxxx" 分组用区间表示为 [3,6]

我们称所有包含大于或等于三个连续字符的分组为 较大分组

找到每一个 较大分组 的区间,按起始位置下标递增顺序排序后,返回结果。

思路分析

双指针

一个指针标记区间开始,一个指针标记区间结束

class Solution {
    public List<List<Integer>> largeGroupPositions(String s) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> t = new ArrayList<>();
        int l = 0, r = 1;
        while (l < s.length()) {
            if (r < s.length() && s.charAt(r) == s.charAt(r - 1)) {
                // 区间扩张
                r++;
            } else {
                if (r - l >= 3) {
                    // 区间长度大于等于3,收集结果
                    t.add(l);
                    t.add(r - 1);
                    ans.add(new ArrayList<>(t));
                    t = new ArrayList<>();
                }
                l = r;
                r = l + 1;
            }
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值