链接:LeetCode
[Leetcode]2000. 反转单词前缀
给你一个下标从 0 开始的字符串 word 和一个字符 ch 。找出 ch 第一次出现的下标 i ,反转 word 中从下标 0 开始、直到下标 i 结束(含下标 i )的那段字符。如果 word 中不存在字符 ch ,则无需进行任何操作。
例如,如果 word = "abcdefd" 且 ch = "d" ,那么你应该 反转 从下标 0 开始、直到下标 3 结束(含下标 3 )。结果字符串将会是 "dcbaefd" 。
返回 结果字符串 。
暴力即可。
class Solution {
public String reversePrefix(String word, char ch) {
for (int i=0; i<word.length(); i++) {
if (word.charAt(i) == ch) return reverse(word.substring(0, i+1)) + word.substring(i+1, word.length());
}
return word;
}
private String reverse(String word) {
char[] arr = word.toCharArray();
int left = 0, right = arr.length-1;
while (left < right) {
char temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++; right--;
}
return String.valueOf(arr);
}
}
[Leetcode]2001. 可互换矩形的组数
用一个下标从 0 开始的二维整数数组 rectangles 来表示 n 个矩形,其中 rectangles[i] = [widthi, heighti] 表示第 i 个矩形的宽度和高度。
如果两个矩形 i 和 j(i < j)的宽高比相同,则认为这两个矩形 可互换 。更规范的说法是,两个矩形满足 widthi/heighti == widthj/heightj(使用实数除法而非整数除法),则认为这两个矩形 可互换 。
计算并返回 rectangles 中有多少对 可互换 矩形。
宽高比,我们可以直接算出来。但是直接用内建的高精度小数的话,很可能会被卡精度。
所以我们把宽高比化成有理数,即宽和高都除以他们的最大公约数。然后用一个hashmap去算同样宽高比的矩形的数量,再之后就转化成一个组合问题啦。
即宽高比相同的矩形有N个,那么他们组成多少个可互换对呢?
很显然,答案是 N*(N-1)/2
class Solution {
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public long interchangeableRectangles(int[][] arr) {
Map<Long, Long> map = new HashMap<Long, Long>();
final long BASE = (long)1e8;
for (var p : arr) {
int a = p[0], b = p[1];
int div = gcd(a, b);
// String frac = (a / div) + "/" + (b / div);
// 使用数字压缩 [x, y] 比较快
long frac = (a / div) * BASE + (b / div);
map.put(frac, map.getOrDefault(frac, 0L) + 1);
}
long res = 0L;
for (var x : map.values()) {
res += x * (x - 1) / 2;
}
return res;
}
}
[Leetcode]2002. 两个回文子序列长度的最大乘积
给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。
请你返回两个回文子序列长度可以达到的 最大乘积 。
子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。
看到2 <= s.length <= 12,就应该能想到状态压缩,通过状态压缩我们可以拿到所有的子序列,然后就判断乘积最大的最长子序列即可。总的来说,还是暴力求解,另外加了一些位运算。
class Solution {
public int maxProduct(String s) {
var total = getTotal(s);
int n = total.size();
int res = 1, leng = s.length();
for(int i=0;i<n-1;++i){
for(int j=i+1;j<n;++j){
int num1 = (int)total.get(i), num2=(int)total.get(j);
if(check(num1,num2,leng)){
res = Math.max(res,getNum(num1,leng)*getNum(num2,leng));
}
}
}
return res;
}
public ArrayList getTotal(String s){
int n = s.length();
int m = 1<<n;
ArrayList total = new ArrayList<Integer>();
for(int i=1;i<m;++i)
{
String tmp = "";
for(int ind=0;ind<n;++ind){
if((i&(1<<ind))!=0){
tmp += s.charAt(ind);
}
}
if(getReverse(tmp)){
total.add(i);
}
}
return total;
}
public int getNum(int num,int n) {
int res = 0;
for(int ind=0;ind<n;++ind) {
if((num&(1<<ind))!=0){
res ++;
}
}
return res;
}
public boolean check(int num1, int num2, int n) {
for (int i=0;i<n;++i){
if((num1&(1<<i))!=0 && (num2&(1<<i))!=0){
return false;
}
}
return true;
}
public boolean getReverse(String s){
int i=0,j=s.length()-1;
while(i<=j)
{
if(s.charAt(i)!=s.charAt(j)){
return false;
}
i++;
j--;
}
return true;
}
}
[Leetcode]2003. 每棵子树内缺失的最小基因值
有一棵根节点为 0 的 家族树 ,总共包含 n 个节点,节点编号为 0 到 n - 1 。给你一个下标从 0 开始的整数数组 parents ,其中 parents[i] 是节点 i 的父节点。由于节点 0 是 根 ,所以 parents[0] == -1 。
总共有 105 个基因值,每个基因值都用 闭区间 [1, 105] 中的一个整数表示。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是节点 i 的基因值,且基因值 互不相同 。
请你返回一个数组 ans ,长度为 n ,其中 ans[i] 是以节点 i 为根的子树内 缺失 的 最小 基因值。
节点 x 为根的 子树 包含节点 x 和它所有的 后代 节点。
如果一棵子树里没有 1,显然这个子树的答案为 1。通过一次 dfs 找出子树内有没有 1 即可完成这一步。
注意到树上的基因值互不相同,因此有 1 的子树的根形成一条从节点 0 出发,到基因值为 1 的节点的链。接下来计算这条链上的答案即可.
显然链上以深度较浅的节点为根的子树,完全包含以深度较深的节点为根的子树。因此链上的节点的答案从深到浅一定是不降(non-decreasing)的。因此我们再一次通过 dfs,从深到浅遍历链上的节点,并维护以下变量:
变量 now:表示链上算出答案的节点中,答案最大是多少。
数组 vis:表示完成遍历的子树中出现过哪些数。
遍历节点 uu 时,首先递归计算同在链上的子节点 vv 的答案,然后遍历不在链上的子节点 ww,并将以 ww 为根的子树中的所有数加入 vis。最后不断递增 now 直到找到第一个 vis[now] 为 false 的数,即为 uu 的答案。
两次 dfs 复杂度均为 \(\mathcal{O}(n)\),now 的值只增不降且最大为 (n + 1),因此整体复杂度\(\mathcal{O}(n)\)。
class Solution {
int n;
int[] parent;
int[] num;
boolean[] exist;
int[] ans;
int p = 1;
Map<Integer, List<Integer>> edges;
public int[] smallestMissingValueSubtree(int[] parents, int[] nums) {
n = nums.length;
parent = parents;
num = nums;
exist = new boolean[n + 2];
ans = new int[n];
edges = new HashMap<>();
for (int i = 0; i < n; i++) {
edges.putIfAbsent(parents[i], new ArrayList<>());
edges.get(parents[i]).add(i);
}
// 标记完后,计算出不含1的子树的答案都是1,此时含1的子树答案还是0
dfsAbout1(0);
// 计算含1的子树的答案是多少
dfsAns(0);
return ans;
}
boolean dfsAbout1(int root) {
List<Integer> children = edges.get(root);
boolean res = false;
if (children != null) {
for (int child : children) {
res |= dfsAbout1(child);
}
}
if (num[root] == 1) res = true;
if (!res) {
ans[root] = 1;
}
return res;
}
void dfsAns(int root) {
List<Integer> children = edges.get(root);
if (children != null) {
// 先递归处理含1的子树
for (int child : children) {
if (ans[child] != 1) {
dfsAns(child);
}
}
// 处理不含1的子树,记录当前子树内含有哪些整数
for (int child : children) {
if (ans[child] == 1) {
dfsAdd(child);
}
}
}
// 记录当前子树根节点的整数
exist[num[root]] = true;
// 查找当前缺失的第一个整数
while (exist[p]) {
p++;
}
ans[root] = p;
}
void dfsAdd(int root) {
exist[num[root]] = true;
List<Integer> children = edges.get(root);
if (children != null) {
for (int child : children) {
dfsAdd(child);
}
}
}
}