1647. Make Character Frequencies Unique

1647. Make Character Frequencies Unique

Question

A string s is called good if there are no two different characters in s that have the same frequency.

Given a string s, return* the minimum number of characters you need to delete to make s good.*

The frequency of a character in a string is the number of times it appears in the string. For example, in the string "aab", the frequency of 'a' is 2, while the frequency of 'b' is 1.

Solution

数组统计+哈希表
用数组统计记录每个字符出现的数量。
count记录需要减少的字符数量。

遍历统计数组,如果哈希表中已经记录,则将数组统计减少,直到归零。
每减少一次则为count加一。
如果哈希表中未记录,则将当前数字添加到哈希表中。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int minDeletions(String s) {
int count = 0;
HashSet<Integer> set = new HashSet<>();
int[] bin = new int[26];
for(char c : s.toCharArray()){
bin[c - 'a']++;
}

for(int i = 0; i < 26; i++){
while(bin[i] !=0 && set.contains(bin[i])){
bin[i]--;
count++;
}
set.add(bin[i]);
}
return count;
}
}
1689. Partitioning Into Deci-Binary Numbers

1689. Partitioning Into Deci-Binary Numbers

Question

A decimal number is called deci-binary if each of its digits is either 0 or 1 without any leading zeros. For example, 101 and 1100 are deci-binary, while 112 and 3001 are not.

Given a string n that represents a positive decimal integer, return the minimum number of positive deci-binary numbers needed so that they sum up to n.

Solution

遍历所有字符,返回字符串中的最大整数。

Code

1
2
3
4
5
6
7
8
9
class Solution {
public int minPartitions(String n) {
char max = '0';
for(char s : n.toCharArray()){
if(s > max) max = s;
}
return max - '0';
}
}

2321. Maximum Score Of Spliced Array

Question

You are given two 0-indexed integer arrays nums1 and nums2, both of length n.

You can choose two integers left and right where 0 <= left <= right < n and swap the subarray nums1[left...right] with the subarray nums2[left...right].

  • For example, if nums1 = [1,2,3,4,5] and nums2 = [11,12,13,14,15] and you choose left = 1 and right = 2, nums1 becomes [1,<strong><u>12,13</u></strong>,4,5] and nums2 becomes [11,<strong><u>2,3</u></strong>,14,15].

You may choose to apply the mentioned operation once or not do anything.

The score of the arrays is the maximum of sum(nums1) and sum(nums2), where sum(arr) is the sum of all the elements in the array arr.

Return the maximum possible score.

A subarray is a contiguous sequence of elements within an array. arr[left...right] denotes the subarray that contains the elements of nums between indices left and right (inclusive).

Solution

滑动窗口。一开始想用dp解决的。
后来发现可以优化到直接记录连续的最大差值即可。
以后有提到连续的一定要想到滑动窗口实现。

辅助方法getMinContigouousDiff()用来计算两个数组间连续的最小的差值和。
当sum为负数时,窗口向右侧扩展,将新的位置加入sum,并更新最小值min。
当扩展后sum大于0时,则放弃之前的所有元素,将sum归零。
最后返回最小值min。

计算两个数组的所有数字之和t1和t2。分别计算nums1[]和nums2[]的差min1和nums2[]和nums1[]的差min2。
在其中替换连续的部分则为t1-min1,t2-min2。取两者中较大的数字返回即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int maximumsSplicedArray(int[] nums1, int[] nums2) {
int n = nums1.length;
int t1 = 0, t2 = 0;
for(int i = 0; i < n ;i++){ //计算两个数组未替换前的和
t1 += nums1[i];
t2 += nums2[i];
}
int min1 = getMinContiguousDiff(nums2, nums1);
int min2 = getMinContiguousDiff(nums1, nums2);
return Math.max(t1 - min1, t2 - min2); //替换连续的部分,两者中取最大值
}

private int getMinContiguousDiff(int[] nums1, int[] nums2){ //滑动窗口,求连续元素间的最小差值
int sum = 0, min = 0;
for(int i = 0; i < nums1.length; i++){
if(sum <= 0){ //当sum为负数时,窗口向右侧扩展,将新的位置加入sum
int diff = nums2[i] - nums1[i];
sum += diff;
if(diff < 0) min = Math.min(min, sum); //更新最小值
if(sum > 0) sum = 0; //当sum为正数时,抛弃前面的元素,重新开始计算sum
}
}
return min;
}
}

2320. Count Number of Ways to Place Houses

Question

There is a street with n * 2 plots, where there are n plots on each side of the street. The plots on each side are numbered from 1 to n. On each plot, a house can be placed.

Return the number of ways houses can be placed such that no two houses are adjacent to each other on the same side of the street. Since the answer may be very large, return it modulo 10<sup>9</sup><span> </span>+ 7.

Note that if a house is placed on the i<sup>th</sup> plot on one side of the street, a house can also be placed on the i<sup>th</sup> plot on the other side of the street.

Solution

计算单侧河道可以放置的种类。
一开始用的是DFS搜素来计算可以放置的种类。
计算后发现是以(2,3)开始的斐波那契数列。

因此直接计算n位置的斐波那契数列值即可。
然后两边可以匹配的种类就是其平方。

记忆化搜索

在计算斐波那契数列时,由于要多次重复计算前面的位置,因此可以采用记忆化搜索记录对应的斐波那契额值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
long[] memo;
public int countHousePlacements(int n) {
memo = new long[n+1];
long res = fibo(n);
res *= res;
res %= 1000000007;
return (int) res;
}

private long fibo(int n){
if(n == 1) return 2;
else if(n == 2) return 3;
if(memo[n] != 0) return memo[n];
memo[n] = fibo(n-1) % 1000000007 + fibo(n-2) % 1000000007;

return memo[n];
}
}

2319. Check if Matrix Is X-Matrix

Question

A square matrix is said to be an X-Matrix if both of the following conditions hold:

  1. All the elements in the diagonals of the matrix are non-zero.
  2. All other elements are 0.

Given a 2D integer array grid of size n x n representing a square matrix, return true* if grid is an X-Matrix*. Otherwise, return false.

Solution

遍历,直接判断是否在对角线上,如果在且位置为0,则返回false。
如果不在对角线上,且位置部位0,则返回false。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public boolean checkXMatrix(int[][] grid) {
int n = grid.length;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(i == j || i + j == n - 1){
if(grid[i][j] == 0) return false;
}
else if(grid[i][j] != 0) return false;
}
}
return true;
}
}

1423. Maximum Points You Can Obtain from Cards

Question

There are several cards arranged in a row, and each card has an associated number of points. The points are given in the integer array cardPoints.

In one step, you can take one card from the beginning or from the end of the row. You have to take exactly k cards.

Your score is the sum of the points of the cards you have taken.

Given the integer array cardPoints and the integer k, return the maximum score you can obtain.

Solution 1

这种取两端的问题可以转化为取中间连续k个元素之和最小。

计算所有数字的和sum。
然后用维护一个宽度为k的窗口内的加和temp,并记录其最小值min。

返回sum - min,结果即是取两侧时可以取的最大值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, sum = 0;
for(int point : cardPoints) sum += point; //求所有数值的和
if(k == n) return sum;
int left = 0, right = n-k, temp = 0, min = 0;

for(int i = 0; i < n-k; i++){
temp += cardPoints[i];
min = temp;
}
while(right < n){ //维护窗口内的temp,并更新min
temp += cardPoints[right] - cardPoints[left];;
min = Math.min(min, temp);
right++;
left++;
}
return sum - min;
}
}

Solution 2

分别计算从两侧开始的加和并记录在两个数组left[]和right[]上。

然后分别取两个指针,左侧指针l从0开始遍历到k,右侧指针对应的位置为l+n-k。计算并更新最大值max。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, max = 0;
int[] left = new int[n+1], right = new int[n+1];

for(int i = 0; i < n; i++){
left[i+1] = left[i] + cardPoints[i];
right[n-i-1] = right[n-i] + cardPoints[n-i-1];
}

for(int l = 0; l <= k; l++){
max = Math.max(max, left[l] + right[n-k+l]);
}

return max;
}
}

2318. Number of Distinct Roll Sequences

Question

You are given an integer n. You roll a fair 6-sided dice n times. Determine the total number of distinct sequences of rolls possible such that the following conditions are satisfied:

  1. The greatest common divisor of any adjacent values in the sequence is equal to 1.
  2. There is at least a gap of 2 rolls between equal valued rolls. More formally, if the value of the i<sup>th</sup> roll is equal to the value of the j<sup>th</sup> roll, then abs(i - j) > 2.

Return the* total number** of distinct sequences possible*. Since the answer may be very large, return it modulo 10<sup>9</sup><span> </span>+ 7.

Two sequences are considered distinct if at least one element is different.

Solution

DFS+记忆化搜索剪枝。
辅助方法getValidNext计算前两个位置为last1和last2时的所有的下一个有效值,将返回值储存在next[][]数组中。

DFS搜索

DFS搜索,传入剩余骰子数量n,前两个骰子的值x和y。
从next[x][y]中得到所有有效值。并进行递归。
将返回结果加和并返回。
当n等于0时,只有一个组合,因此返回1。

记忆化搜索

在搜索时会重复计算很多相同的x,y与n的组合,因此可以采用记忆化搜索进行剪枝。
用memo[][][]数组记录x,y与n的状态。如果此前已经计算过其结果,则直接返回memo[x][y][n]。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution {
List<Integer>[][] next;
int[][][] memo;
public int distinctSequences(int n) {
next = new ArrayList[7][7];
for(int i = 0; i <= 6; i++){
for(int j = 0; j <= 6; j++){
next[i][j] = getValidNext(i, j);
}
}
memo = new int[7][7][10001];
return count(n, 0, 0);
}

private int count(int n, int x, int y){
if(n == 0) return 1;
if(memo[x][y][n] != 0) return memo[x][y][n];
List<Integer> validNext = next[x][y];
int sum = 0;
for(int z : validNext){
sum += count(n-1, y, z);
sum %= 1000000007;
}
memo[x][y][n] = sum;
return sum;
}

private List<Integer> getValidNext(int last1, int last2){
List<Integer> arr = new ArrayList<>();
for(int i = 1; i <= 6; i++){
if(last1 == 0 && last2 == 0) arr.add(i);
else if(i == last1 || i == last2) continue;
else if((last2 & 1) == 0 && (i & 1) == 0) continue;
else if((last2 == 3 || last2 == 6) && (i == 3 || i == 6)) continue;
else arr.add(i);
}
return arr;
}
}

2317. Maximum XOR After Operations

Question

You are given a 0-indexed integer array nums. In one operation, select any non-negative integer x and an index i, then update nums[i] to be equal to nums[i] AND (nums[i] XOR x).

Note that AND is the bitwise AND operation and XOR is the bitwise XOR operation.

Return the maximum possible bitwise XOR of all elements of nums after applying the operation any number of times.

Solution

异或运算相当于不进位的加和
和运算相当于掩码操作
或运算相当于保留该位上的1

由于我们要对所有数字进行异或运算,因此只要能改变某一位对应的数字,我们就可以确保这一位在进行异或运算时结果可以为1。(当不改变时改为的异或运算结果为0,则我们只需要改变改为即可得到1)

将所有我们可以操作的位置变为1,就可以得到最大值。

因此,我们只需要确定哪些位是我们可以操作的即可:

  • nums[i]与任意正整数进行异或运算可以得到任意整数。在这个条件下我们可以得到任意的32位整数。
  • 然而和运算相当于掩码操作,如果nums[i]对应二进制的某一位上位0,则我们无法通过和运算将这一位改变为1。

只要该位出现过1,则我们可以控制这个位。因此我们可以通过或运算所有数字,保留所有可控制的位。

Code

1
2
3
4
5
6
7
8
9
class Solution {
public int maximumXOR(int[] nums) {
int sum = 0;
for(int num : nums){
sum |= num;
}
return sum;
}
}

2316. Count Unreachable Pairs of Nodes

Question

You are given an integer n. There is an undirected graph with n nodes, numbered from 0 to n - 1. You are given a 2D integer array edges where edges[i] = [a<sub>i</sub>, b<sub>i</sub>] denotes that there exists an undirected edge connecting nodes a<sub>i</sub> and b<sub>i</sub>.

Return the number of pairs of different nodes that are unreachable from each other.

Solution

并查集,将所有元素union,然后计算每个元素所在集之外的元素和相加即可。
注意由于是无向图,因此计算加和时每两个点之间都计算了两次,因此需要将结果除以2。

路径压缩

在进行find()查找时,将经过的每一个节点设置为根节点。
这样做可以有效的降低树高,减少操作次数。
采用递归的形式实现。

按秩合并

用size[]数组记录当前位置的节点数量。
在合并两个数字时,将秩较小的数字指向秩较大的数字。
并更新根节点的size[]值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution {
public long countPairs(int n, int[][] edges) {
UnionFind uf = new UnionFind(n);
long res = 0;
for(int[] edge : edges){
uf.union(edge[0], edge[1]);
}

for(int i = 0; i < n; i++){
res += ( n - uf.getSize(i) );
}
return res/2; //无向图,因此每两个节点计算了两次
}

class UnionFind {
int[] parent;
int[] size;

public UnionFind(int n){
parent = new int[n];
size = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
size[i] = 1;
}
}

private int find(int id){
return parent[id] == id ? id : find(parent[id]); //路径压缩,将路径上的所有节点归结到根节点上
}

private boolean union(int id1, int id2){
int p1 = find(id1);
int p2 = find(id2);
if(p1 == p2) return false;
if(size[p1] > size[p2]){ //按秩进行压缩
parent[p2] = p1;
size[p1] += size[p2];
}
else{
parent[p1] = p2;
size[p2] += size[p1];
}
return true;
}

public int getSize(int num){
return size[find(num)];
}
}
}****

2315. Count Asterisks

Question

You are given a string s, where every two consecutive vertical bars '|' are grouped into a pair. In other words, the 1st and 2nd '|' make a pair, the 3rd and 4th '|' make a pair, and so forth.

Return *the number of '*' in s, excluding the '*' between each pair of *'|'.

Note that each '|' will belong to exactly one pair.

Solution

统计“|”字符出现的数量,如果数量为偶数时,则计算出现的“*”符号。

Code

1
2
3
4
5
6
7
8
9
10
class Solution {
public int countAsterisks(String s) {
int num = 0, count = 0;
for(char c : s.toCharArray()){
if(c == '|') num++;
else if((num & 1) == 0 && c == '*') count++;
}
return count;
}
}
665. Non-decreasing Array

665. Non-decreasing Array

Question

Given an array nums with n integers, your task is to check if it could become non-decreasing by modifying at most one element.

We define an array is non-decreasing if nums[i] <= nums[i + 1] holds for every i (0-based) such that (0 <= i <= n - 2).

Solution 1

记录两个最大值last1和last2,初始化为整数最小值。真值flag初始化为true用来记录是否已经有非单调递增的值出现。
遍历数组,如果当前数字num大于等于last2,则符合单调递增,更新last1和last2。

如果num小于last2,则不符合单调递增,此时将flag置为false。

  • 如果num大于等于last1,则跳过现有的last2,将num保存在last2上。
  • 如果num小于,则保留当前的last2

如果flag已经为false,则非单调递增的数字超过1个,返回false。
遍历完毕则返回true。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean checkPossibility(int[] nums) {
boolean flag = true;
int last1 = Integer.MIN_VALUE, last2 = Integer.MIN_VALUE;

for(int num : nums){
if(num >= last2){
last1 = last2;
last2 = num;
}
else if(flag){
flag = false;
if(num >= last1) last2 = num;
else continue;
}
else{
return false;
}
}
return true;
}
}

Solution 2

原理和Solution 1相同,只不过采用单调栈的形式保存单调递增数字。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean checkPossibility(int[] nums) {
Stack<Integer> s = new Stack<>();
boolean flag = true;

for(int num : nums){
if(s.isEmpty() || num >= s.peek()) s.push(num);
else if(flag){
flag = false;
int temp = s.pop();
if(s.isEmpty() || num >= s.peek()) s.push(num);
else s.push(temp);
}
else{
return false;
}
}
return true;
}
}
630. Course Schedule III

630. Course Schedule III

Question

There are n different online courses numbered from 1 to n. You are given an array courses where courses[i] = [duration<sub>i</sub>, lastDay<sub>i</sub>] indicate that the i<sup>th</sup> course should be taken continuously for duration<sub>i</sub> days and must be finished before or on lastDay<sub>i</sub>.

You will start on the 1<sup>st</sup> day and you cannot take two or more courses simultaneously.

Return the maximum number of courses that you can take.

Solution

贪心算法,优先级队列实现。
优先选择截止日期更短的课程。
优先级队列pq记录当前已选课程,按持续时间从长到短排列。
同时维护当前的日期curDay。

当当前选择的课程截止日期晚于curDay加上当前的持续时间duration时,查看大根堆里最长的课程longestDuration。如果最长的时间大于当前时间,则将其挤出,并将当前课程加入。

如果当前课程截止日期晚于curDay,则直接将其加入队列中,并更新curDay。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int scheduleCourse(int[][] courses) {
Arrays.sort(courses, (a, b) -> a[1] - b[1]);
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> b[0] - a[0]);
int curDay = 0;
for(int[] course : courses){
int duration = course[0];
int lastDay = course[1];

if(curDay + duration > lastDay){ //大于截止日期
if(pq.isEmpty()) continue;
int longestDuration = pq.peek()[0];
if(longestDuration > duration){ //替换到当前队列中持续日期最长的课程
curDay -= pq.poll()[0];
curDay += duration;
pq.offer(course);
}
}
else{ //小于截止日期则加入选课
curDay += duration;
pq.offer(course);
}
}
return pq.size();
}
}
1354. Construct Target Array With Multiple Sums

1354. Construct Target Array With Multiple Sums

Question

You are given an array target of n integers. From a starting array arr consisting of n 1’s, you may perform the following procedure :

  • let x be the sum of all elements currently in your array.
  • choose index i, such that 0 <= i < n and set the value of arr at index i to x.
  • You may repeat this procedure as many times as needed.

Return true if it is possible to construct the target array from arr, otherwise, return false.

Solution 1

递归,每次寻找到数组中最大的数字的下标maxIndex,并对整个数组加和。
当数组中出现小于1的数时,不成立,返回false。
当数组加和等于数组长度时,返回true。

记录剩余的数字others,如果没有剩余数字,则返回false。
将target[maxIndex]减去others,因此每次翻倍,快速逼近。
当小于0时,则还原到上一个target[maxIndex]。

递归更改后的数组。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public boolean isPossible(int[] target) {
int maxIndex = 0, sum = 0;
for(int i = 0; i < target.length; i++){
if(target[i] <= 0) return false; //数组中出现小于1的数,则不成立,返回false
if(target[maxIndex] < target[i]) maxIndex = i;
sum += target[i];
}
if(sum == target.length) return true; //加和等于长度,则数组内全部为1,返回true
int others = sum - target[maxIndex];
if(others == 0) return false; //没有其他值时返回false
target[maxIndex] -= others;
int n = 1;
while(target[maxIndex] > others){
target[maxIndex] -= n * others;
if(target[maxIndex] <= 0){
target[maxIndex] += n * others;
break;
}
n*=2; //因子每次翻倍,快速逼近
}
return isPossible(target);
}
}

Solution 2

优先级队列, 大根堆。
每次挤出队列里最大的数字max。

如果max的值为1,则返回true。

计算加和和新的值,并更新sum。
如果最大的数字max小于0,或者剩余的sum小于0,则返回false。

当max仍然大于剩下的数字时,继续做减法。采用因数翻倍,快速接近目标值。
如果max小于0,则恢复到上一个值,并添加到优先级队列中。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public boolean isPossible(int[] target) {
PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
int sum = 0;
for(int num : target){
pq.offer(num);
sum+=num;
}
while(true){
int max = pq.poll();
if(max == 1) return true;

sum -= max;
max = max - sum;
if(sum <= 0 || max <= 0) return false;

int n = 1;
while(max > sum){
max -= n * sum;
if(max <= 0){
max += n * sum;
break;
}
n*=2;
}
sum += max;
pq.offer(max);
}
}
}
771. Jewels and Stones

771. Jewels and Stones

Question

You’re given strings jewels representing the types of stones that are jewels, and stones representing the stones you have. Each character in stones is a type of stone you have. You want to know how many of the stones you have are also jewels.

Letters are case sensitive, so "a" is considered a different type of stone from "A".

Solution

数组统计,先遍历宝石,记录宝石的位置。
然后遍历石头,如果对应的位置已被记录则计数加一。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int numJewelsInStones(String jewels, String stones) {
int ret = 0;
int[] bin = new int[58];
for(char c : jewels.toCharArray()){
bin[c-'A']++;
}

for(char c : stones.toCharArray()){
if(bin[c-'A'] != 0) ret++;
}
return ret;
}
}
1108. Defanging an IP Address

1108. Defanging an IP Address

Question

Given a valid (IPv4) IP address, return a defanged version of that IP address.

A defanged IP address replaces every period "." with "[.]".

Solution

迭代每个字符,用StringBuffer类来组成新的字符串。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public String defangIPaddr(String address) {
StringBuffer sb = new StringBuffer();
for(char c : address.toCharArray()){
if(c == '.'){
sb.append("[.]");
}
else{
sb.append(c);
}
}
return sb.toString();
}
}