LeetCode1442. 形成两个异或相等数组的三元组数目 / 剑指 Offer 46. 把数字翻译成字符串 / 剑指 Offer 47. 礼物的最大价值

1442. 形成两个异或相等数组的三元组数目

2021.5.18每日一题

题目描述
给你一个整数数组 arr 。

现需要从数组中取三个下标 i、j 和 k ,其中 (0 <= i < j <= k < arr.length) 。

a 和 b 定义如下:

a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]
b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]
注意:^ 表示 按位异或 操作。

请返回能够令 a == b 成立的三元组 (i, j , k) 的数目。

示例 1:

输入:arr = [2,3,1,6,7]
输出:4
解释:满足题意的三元组分别是 (0,1,2), (0,2,2), (2,3,4) 以及 (2,4,4)
示例 2:

输入:arr = [1,1,1,1,1]
输出:10
示例 3:

输入:arr = [2,3]
输出:0
示例 4:

输入:arr = [1,3,5,7,9]
输出:3
示例 5:

输入:arr = [7,11,12,9,5,2,7,17,22]
输出:8

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-triplets-that-can-form-two-arrays-of-equal-xor
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

第一眼看到的思路,预处理前缀异或数组,方便取任意区间的异或结果
然后三层循环,定位三个点进行计算,尽然也过了,超过17.45%,想想怎么优化

class Solution {
    public int countTriplets(int[] arr) {
        //应该是先预处理了
        int l = arr.length;
        int[] pre = new int[l + 1];

        //前i位的异或结果,方便取任意区间异或结果
        for(int i = 1; i <= l; i++){
            pre[i] = pre[i - 1] ^ arr[i - 1];
        }

        //三个点,难道要三层循环吗
        //要求方案数,想想动态规划
        //先暴力写一下
        int res = 0;
        for(int j = 1; j < l; j++){
            for(int i = 0; i < j; i++){
                for(int k = j; k < l; k++){
                    //i到j - 1, j 到k是否相同
                    if((pre[i] ^ pre[j]) == (pre[j] ^ pre[k + 1]))
                        res++;
                }
            }
        }
        return res;
    }
}

然后看了一眼提示,又是自我怀疑的几秒钟
两个数组异或相等,那么这两个数组异或结果肯定为0
而一组数异或为0,那么将这个数组按随便一个节点分割成两部分,那么这两部分的异或结果都是相同的
所以两层循环

class Solution {
    public int countTriplets(int[] arr) {
        //应该是先预处理了
        int l = arr.length;
        int[] pre = new int[l + 1];

        //前i位的异或结果,方便取任意区间异或结果
        for(int i = 1; i <= l; i++){
            pre[i] = pre[i - 1] ^ arr[i - 1];
        }

        //两层循环,找异或结果为0的区间
        int res = 0;
        for(int i = 0; i < l - 1; i++){
            for(int j = i + 1; j < l; j++){
                if((pre[i] ^ pre[j + 1]) == 0)
                    res += j - i;
            }
        }
        return res;
    }
}

万万没有想到还能一层循环
首先pre[i] ^ pre[j + 1] == 0 也可以写成pre[i] = pre[j + 1]
若对于下标 j , 满足条件的 i 为i1, i2, i3… im, 那么可取的区间(三元组)数目为:
j - i1 + j - i2 + … + j - im = m * j - (i1 + i2 + … + im)
因此遍历到下标j时,需要知道 m 和所有满足条件的下标 i 之和
可以采用两个哈希表来存储这两个结果

class Solution {
    public int countTriplets(int[] arr) {
        //应该是先预处理了
        int l = arr.length;
        int[] pre = new int[l + 1];

        //前i位的异或结果,方便取任意区间异或结果
        for(int i = 1; i <= l; i++){
            pre[i] = pre[i - 1] ^ arr[i - 1];
        }
        //首先pre[i] ^ pre[j + 1] == 0 也可以写成pre[i] = pre[j + 1]
        //若对于下标 j , 满足条件的 i 为i1, i2, i3... im, 那么可取的区间(三元组)数目为:
        //j - i1 + j - i2 + ....  + j - im = m * j - (i1 + i2 + ... + im)
        //单层循环,用哈希表找m和满足条件的i的和
        Map<Integer, Integer> count = new HashMap<>();
        Map<Integer, Integer> sum = new HashMap<>();
        int res = 0;
     
        for(int i = 0; i < l; i++){
        	//这里的下标必须是i+1
            if(count.containsKey(pre[i + 1])){
                res += count.get(pre[i + 1]) * i - sum.get(pre[i + 1]);
            }
            //如果有这个数,就加1
            count.put(pre[i], count.getOrDefault(pre[i], 0) + 1);
            sum.put(pre[i], sum.getOrDefault(pre[i], 0) + i);
        }
        return res;
    }
}

剑指 Offer 46. 把数字翻译成字符串

题目描述
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。


示例 1:

输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

转换成字符串,动态规划

class Solution {
    public int translateNum(int num) {
        //因为只需要统计翻译种数,而不需要具体的翻译方案,因此动态规划可以
        //dp[i] = dp[i - 1] + dp[i - 2](如果是两位的话)

        String s = String.valueOf(num);
        int l = s.length();
        int[] dp = new int[l + 1];
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 1; i < l; i++){
            int k = 26;
            if(i > 0){
                char cur = s.charAt(i);
                char pre = s.charAt(i - 1);
                if(pre != '0'){
                    String ss = pre + "" + cur;
                    k = Integer.valueOf(ss);
                }
            }
            dp[i + 1] = dp[i];
            if(k <= 25)
                dp[i + 1] += dp[i - 1];
        }
        return dp[l];
    }
}

想想不转换成字符串,从低位到高位进行处理可以吗,可以

class Solution {
    public int translateNum(int num) {
        //因为只需要统计翻译种数,而不需要具体的翻译方案,因此动态规划可以
        //dp[i] = dp[i - 1] + dp[i - 2](如果是两位的话)

        //从低位到高位,用数字直接处理
        int cur = num % 10; //个位
        int pre = cur;		//前面一位
        int res = 1;		

        int dp1 = 1;		//两个状态量,分别记录dp[1]和dp[0]
        int dp0 = 1;
        //处理十位
        num /= 10;
        while(num > 0){
            cur = num % 10;		//当前位数
            res = dp1;			
            //如果能和前一位拼接
            if(cur != 0 && cur * 10 + pre <= 25)
                res += dp0;
            dp0 = dp1;
            dp1 = res;
            pre = cur;
            num /= 10;
        }
        return res;
    }
}

剑指 Offer 47. 礼物的最大价值

题目描述
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?


示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

因为只能从左面和上面转移,所以没必要深搜,还是动态规划
另外,多开辟一行一列的空间可以使代码更加简洁,这里就不写了

class Solution {
    public int maxValue(int[][] grid) {
        //同样是动态规划,因为只能从上面和左面转移过来
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        
        dp[0][0] = grid[0][0];
        for(int i = 1; i < m; i++){
            dp[i][0] = grid[i][0] + dp[i - 1][0];
        }

        for(int j = 1; j < n; j++){
            dp[0][j] = grid[0][j] + dp[0][j - 1];
        }

        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[m - 1][n - 1];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值