LeetCode困难题两道

文章讲述了如何解决寻找树中形成回文字符串的路径数量的问题,采用深度优先搜索(DFS)结合位运算的方法来统计路径,并通过维护一个哈希映射存储中间结果。此外,还介绍了使用贪心算法解决数组长度递增组的最大数目的问题,两种方法都涉及到高效的数据结构和算法应用。
摘要由CSDN通过智能技术生成

树中可以形成回文的路径数

这题很考验思维量。
题意总结一下就是:
提供一颗树,每两个节点之间形成一条路径,该路径的值为字符串,要求求出树的所有路径形成的字符串重新排列成回文串的个数有多少个。

  • 字符串的每一个字符的取值范围为a - z

思路:
很容易想得到,路径的含义可以用26长度的二进制数组来表示。则当前某一路径 ^ x的值需要等于20,21,22 … 226
则可以通过下列代码获取当前字符串所能匹配到的所有x的数量

for (Integer key : check(curValue)) {  
    sum += countMap.getOrDefault(key, 0);  
}
private List<Integer> check(int num) {  
    // num ^ x -> 0 或者 2的幂次方  
    // 则枚举 0 以及 2的幂次方,倒推x的集合  
    ArrayList<Integer> list = new ArrayList<Integer>(){{add(num);}};  
    int bit = 1;  
    for (int i = 0; i < 26; i++) {  
        list.add(num ^ bit);  
        bit <<= 1;  
    }  
    return list;  
}

那维护map的key - value就成为了关键。
从异或的定义上来说,我们可以得知,任意的路径 ^ x 可以表示为 a --> b的二进制数状态,且 a --> b 可以拆解为 a --> root ^ b --> root
则我们可以把每一条路径视为 a --> 0 ^ 0 --> b
此时可以通过dfs获取所有0 --> b路径的二进制值,并通过map获取符合的回文串的数量,然后再将该路径存放至map中,然后进行下一轮递归,则可求出所有满足是回文串的路径的数量。
完整代码如下所示:

public long countPalindromePaths(List<Integer> parent, String s) {  
    HashMap<Integer, List<Integer>> buildTreeMap = new HashMap<>();  
    for (int i = 1; i < parent.size(); i++) {  
        buildTreeMap.computeIfAbsent(parent.get(i), value -> new ArrayList<>()).add(i);  
    }  
    // 存当前所有子节点 -> 0的值,并且初始化map(0) = 1  
    // dfs不会重复计算节点  
    HashMap<Integer, Integer> countMap = new HashMap<Integer, Integer>(){{put(0, 1);}};  
    return dfs(0, buildTreeMap, countMap, s.toCharArray(), 0);  
}  
  
private long dfs(int parent, HashMap<Integer, List<Integer>> buildTreeMap, HashMap<Integer, Integer> countMap, char[] s, int preValue) {  
    List<Integer> list = buildTreeMap.get(parent);  
    long sum = 0;  
    if (list == null) {  
        return sum;  
    }  
    for (int children : list) {  
        int curValue = (1 << s[children] - 'a') ^ preValue;  
        for (Integer key : check(curValue)) {  
            sum += countMap.getOrDefault(key, 0);  
        }  
        countMap.merge(curValue, 1, Integer::sum);  
        sum += dfs(children, buildTreeMap, countMap, s, curValue);  
    }  
    return sum;  
}  
  
private List<Integer> check(int num) {  
    // num ^ x -> 0 或者 2的幂次方  
    // 则枚举 0 以及 2的幂次方,倒推x的集合  
    ArrayList<Integer> list = new ArrayList<Integer>(){{add(num);}};  
    int bit = 1;  
    for (int i = 0; i < 26; i++) {  
        list.add(num ^ bit);  
        bit <<= 1;  
    }  
    return list;  
}

长度递增组的最大数目

可以贪心的想到,数组从大到小排序之后,每一个元素都应该优先占据一列,占据完之后,多的元素往上补,这样的话,模拟的思路一下就豁然开朗了。代码如下:

class Solution {  
    public int maxIncreasingGroups(List<Integer> usageLimits) {  
        int l = 1, r = usageLimits.size();  
        Integer[] integers = usageLimits.toArray(new Integer[0]);  
        Arrays.sort(integers, (a, b) -> Integer.compare(b, a));  
        int max = l;  
        while(l <= r) {  
            int mid = (l + r) >> 1;  
            int count = mid;  
            int pre = 0;  
            for (Integer limit : integers) {  
                if (limit <= count) {  
                    pre += count - limit;  
                } else {  
                    pre = Math.max(pre - limit + count, 0);  
                }  
                if (count != 0) {  
                    count--;  
                }  
            }  
            if (pre == 0) {  
                l = mid + 1;  
                max = mid;  
            } else {  
                r = mid - 1;  
            }  
        }  
        return max;  
    }  
}

看了下别人的解题思路,其实可以正向推导,数组从小到大依次取出元素的值,用贪心的理念可以想到,越小的元素最好是放到越小的列中,那么可以从小到大推导出符合条件的最大的列是多少,代码如下所示:

class Solution {  
    public int maxIncreasingGroups(List<Integer> usageLimits) {  
        Integer[] integers = usageLimits.toArray(new Integer[0]);  
        long cache = 0;  
        Arrays.sort(integers);  
        int ans = 0;  
        for(int limit : integers) {  
            if (limit + cache >= ans + 1) {  
                cache += limit - 1 - ans++;  
            } else {  
                cache += limit;  
            }  
        }  
        return ans;  
    }  
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值