leetcode 第205场周赛

1576. 替换所有的问号

难度:打卡

考点:遍历

时间复杂度:O(n)

class Solution {
    //将字符串存入字符数组,注意首尾多开一个设置为0
    //然后每次遇到‘?’我们就将其变为与前后都不同的字符即可
    public String modifyString(String s) {
        char[] cha = new char[26];
        for(int i = 0; i < 26; ++i){
            cha[i] = (char)('a' + i);
        }
        char[] chs = new char[110];
        int n = s.length();
        chs[0] = '0';
        for(int i = 1; i <= n; ++i){
            chs[i] = s.charAt(i - 1);
        }
        chs[n + 1] = '0';
        for(int i = 1; i <= n; ++i){
            int c = 0;
            if(chs[i] == '?'){
                while(cha[c] == chs[i - 1] || cha[c] == chs[i + 1]){
                    ++c;
                }
            chs[i] = cha[c];
            }
        }
        String str = "";
        for(int i = 1; i <= n; ++i){
            str += chs[i];
        }
        return str;
    }
}

1577. 数的平方等于两数乘积的方法数

难度:打卡

考点:哈希表

时间复杂度:O(n^2)

//哈希表加速即可
class Solution {
    HashMap<Long, Integer>map2 = new HashMap<>();
    HashMap<Long, Integer>map1 = new HashMap<>();
    public int numTriplets(int[] nums1, int[] nums2) {
        int res = 0;
        for(int i = 0; i < nums2.length; ++i){
            for(int j = i + 1; j < nums2.length; ++j){
                long cur = (long)nums2[i] * nums2[j];
                map2.put(cur, map2.getOrDefault(cur, 0) + 1);
            }
        }
        for(int i = 0; i < nums1.length; ++i){
            long cur = (long)nums1[i] * nums1[i];
            if(map2.containsKey(cur)){
                res += map2.get(cur);
            }
        }
        for(int i = 0; i < nums1.length; ++i){
            for(int j = i + 1; j < nums1.length; ++j){
                long cur = (long)nums1[i] * nums1[j];
                map1.put(cur, map1.getOrDefault(cur, 0) + 1);
            }
        }
        for(int i = 0; i < nums2.length; ++i){
            long cur = (long)nums2[i] * nums2[i];
            if(map1.containsKey(cur)){
                res += map1.get(cur);
            }
        }
        return res;
    }
}

1578. 避免重复字母的最小删除成本

难度:打卡

考点:双指针,累加重复字符的权值减去最大权值

时间复杂度:O(n)

class Solution {
    public int minCost(String s, int[] cost) {
        int n = s.length();
        if(n <= 1)return 0;
        char[] chs = s.toCharArray();
        int i = 0, j = 1;
        int res = 0;
        while(i < n && j < n){
            int cur = cost[i];
            int max = cost[i];
            while(j < n && chs[j] == chs[i]){
                cur += cost[j];
                max = Math.max(max, cost[j]);
                ++j;
            }
            if(j - i > 1){
                res += cur - max;
            }
            i = j;
            j++;
        }
        return res;
    }
}

1579. 保证图可完全遍历

难度:中等

考点:并查集、dfs、(克鲁斯卡尔最小生成树的方式其实是并查集)(公共边方法其实是dfs)

时间复杂度O(n + m)

算法思想:先找到3的类型,将其插入到并查集,然后在根据这个并查集分别去添加1类型和2类型,重复的边就计数,到最后所有节点都是在一个节点下的(可以循环也可以先设定n,每次加入一条边就减一表示并查集的节点个数加1),否则就是非连通图,输出结果即可。

并查集做法:

可以先设置联通的点为n,每次联通边添加进并查集则n--即可



class Solution {

    int[] pa = new int[100010];
    int[] pb = new int[100010];
    int find(int x,int[] p){
        if(x != p[x])p[x] = find(p[x], p);
        return p[x];
    }
    boolean add(int a,int b,int[] p){
        int p1 = find(a,p);
        int p2 = find(b,p);
        if(p1 == p2)return true;
        p[p2] = p1;
        return false;
    }
    public int maxNumEdgesToRemove(int n, int[][] edges) {
        int res = 0;
        int cc = n, cb = n;//优化点
        for(int i = 1; i <= n; ++i){
            pa[i] = i;
            pb[i] = i;
        }
        for(int i = 0; i < edges.length; ++i){
            int t = edges[i][0];
            int a = edges[i][1];
            int b = edges[i][2];
            if(t == 3){
                boolean f = add(a, b, pa);
                if(f)res++;
                else{
                    cc--;
                    cb--;
                }
            }
        }
        for(int i = 0; i <= n; ++i)pb[i] = pa[i];
        for(int i = 0; i < edges.length; ++i){
            int t = edges[i][0];
            int a = edges[i][1];
            int b = edges[i][2];
            if(t == 1){
                boolean f1 = add(a, b, pa);
                if(f1)res++;
                else cc--;
            }
            if(t == 2){
                boolean f1 = add(a, b, pb);
                if(f1)res++;
                else cb--;
            }
        }
        if(cc > 1 || cb > 1)return -1;//大于一是因为刚开始并查集就有一个节点,就是根节点
        return res;
    }
}

精妙的dfs(需要分别存储123三种类型边,然后dfs他们)

算法思想:

这道题用普通的dfs就可以解,思路如下:
首先dfs Alice 和 Bob,其中有一位不能dfs走完全部节点,则返回-1.
之后,清空数据,从1节点开始,dfs只走3类型的边,dfs完记录最多能访问多少个节点count。这些节点组成一个可用公共边互通的子集,子集内遍历访问的边数就是count-1;
再找下一个未访问的节点,继续dfs,并继续形成子集,并把所有的子集边数相加为sumBian = sum(count[i] - 1);
这个sum则是Alice和Bob遍历中,能公用的最大边数。
则Alice和Bob 访问使用的边数量为: common = (n - 1) + (n - 1) - sum
最大可删除的边为: edges.length - common = edges.length - (n - 1) * 2 + sum.

作者:huanglin
链接:https://leetcode-cn.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/solution/po-su-de-shen-du-bian-li-si-xiang-fu-za-du-onnwei-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    int N = 100010;
    boolean[] f = new boolean[N];
    ArrayList[][] g = new ArrayList[3][N];
    int cnt = 0;
    public int maxNumEdgesToRemove(int n, int[][] edges) {
        int l = edges.length;
        for(int i = 0; i < 3; ++i){
            for(int j = 0; j <= n; ++j)
                g[i][j] = new ArrayList<Integer>();
        }
        for(int i = 0; i < l; ++i){//建图
            int t = edges[i][0];
            int a = edges[i][1];
            int b = edges[i][2];
            g[t - 1][a].add(b);
            g[t - 1][b].add(a);
        }
        dfs(1, 0);
        if(cnt < n)return -1;
        cnt = 0;
        Arrays.fill(f, false);
        dfs(1, 1);
        if(cnt < n)return -1;
        Arrays.fill(f, false);
        int com = 0;
        for(int i = 1; i <= n; ++i){
            if(!f[i]){
                cnt = 0;
                dfs(i, 2);//2表示不深搜type为1和2的边只搜3的边,找出公共边的长度
                com += cnt - 1;
            }
        }
        return l - (n - 1 << 1) + com;
    }
    
    void dfs(int v, int t){
        if(f[v])return;
        f[v] = true;
        cnt++;
        for(Object i : g[2][v]){//公共:贪心的思想先深搜公共边
            int c = (int)i;
            if(!f[c])
                dfs(c, t);
        }
        if(t == 0 || t == 1){//私自:深搜各自的边
            for(Object i : g[t][v]){
                int c = (int)i;
                if(!f[c])
                    dfs(c, t);
            }
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值