树中可以形成回文的路径数
这题很考验思维量。
题意总结一下就是:
提供一颗树,每两个节点之间形成一条路径,该路径的值为字符串,要求求出树的所有路径形成的字符串重新排列成回文串的个数有多少个。
- 字符串的每一个字符的取值范围为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;
}
}