Post

Greedy Algorithm

Frog Jump II

1
2
3
4
5
6
7
8
9
10
public int maxJump(int[] stones) {
    // it's optimal to use of all rocks
    // it's not optimal to use two consecutive rocks
    // because on the way back, stones[i + 2] - stones[i] > stones[i + 1] - stones[i]
    int cost = stones[1] - stones[0], n = stones.length;
    for (int i = 2; i < n; i++) {
        cost = Math.max(cost, stones[i] - stones[i - 2]);
    }
    return cost;
}

Partition String Into Substrings With Values at Most K

Flower Planting With No Adjacent

No node has more than 3 neighbors, so there’s always one possible color to pick.

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
private final int numFlowers = 4;

public int[] gardenNoAdj(int N, int[][] paths) {
    // constructs the graph
    Map<Integer, List<Integer>> graph = new HashMap<>();
    for (int i = 1; i <= N; i++) {
        graph.put(i, new ArrayList<>());
    }
    for (int[] p : paths) {
        graph.get(p[0]).add(p[1]);
        graph.get(p[1]).add(p[0]);
    }

    int[] result = new int[N];
    for (int i = 1; i <= N; i++) {
        // flowers[i] indicates whether the (i + 1)th flower has been picked
        boolean[] flowers = new boolean[numFlowers];

        // finds the neighbors who have picked a flower
        for (int garden : graph.get(i)) {
            if (result[garden - 1] > 0) {
                flowers[result[garden - 1] - 1] = true;
            }
        }

        // picks the first available flower
        for (int f = 1; f <= numFlowers; f++) {
            if (!flowers[f - 1]) {
                result[i - 1] = f;
                break;
            }
        }    
    }
    return result;
}

Jump Game

1
2
3
4
5
6
7
public boolean canJump(int[] nums) {
    int i = 0;
    for (int reach = 0; i <= reach && i < nums.length; i++) {
        reach = Math.max(reach, i + nums[i]);
    }
    return i == nums.length;
}

Video Stitching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int videoStitching(int[][] clips, int T) {
    Arrays.sort(clips, (a, b) -> a[0] - b[0]);

    int i = 0, start = 0, end = 0, result = 0;
    while (start < T) {
        while (i < clips.length && clips[i][0] <= start) {
            end = Math.max(end, clips[i++][1]);
        }

        if (start == end) {
            return -1;
        }

        start = end;
        result++;
    }

    return result;
}

Broken Calculator

1
2
3
4
5
6
7
8
public int brokenCalc(int startValue, int target) {
    int s = 0;
    while (target > startValue) {
        target = target % 2 == 0 ? target / 2 : target + 1;
        s++;
    }
    return s + startValue - target;
}

Split Array into Consecutive Subsequences

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
public boolean isPossible(int[] nums) {
    int prev = Integer.MIN_VALUE, curr = 0;
    // count of consecutive subsequences with size i, ending at prev/curr
    int p1 = 0, p2 = 0, p3 = 0;
    int c1 = 1, c2 = 0, c3 = 0;

    for (int i = 0; i < nums.length; prev = curr, p1 = c1, p2 = c2, p3 = c3) {
        int count = 0;
        for (curr = nums[i]; i < nums.length && curr == nums[i]; i++) {
            count++;
        }

        // fills the sets greedily: 1 -> 2 -> 3
        if (curr != prev + 1) {
            if (p1 != 0 || p2 != 0) {
                return false;
            }
            c1 = count;
            c2 = 0;
            c3 = 0;
        } else {
            if (count < p1 + p2) {
                return false;
            }
            c1 = Math.max(0, count - (p1 + p2 + p3));
            c2 = p1;
            c3 = p2 + Math.min(p3, count - (p1 + p2));
        }
    }

    return p1 == 0 && p2 == 0;
}

Hand of Straights

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
public boolean isNStraightHand(int[] hand, int W) {
    Map<Integer, Integer> count = new TreeMap<>();
    for (int h : hand) {
        count.put(h, count.getOrDefault(h, 0) + 1);
    }

    // number of groups that are not closed
    int prev = -1, opened = 0;
    // The i-th queue element is the number of opened groups starting from the i-th key
    Queue<Integer> q = new LinkedList<>();
    for (var e : count.entrySet()) {
        int k = e.getKey(), v = e.getValue();
        if ((opened > 0 && k > prev + 1) || opened > v) {
            return false;
        }

        q.add(v - opened);
        prev = k;
        opened = v;
        if (q.size() == W) {
            opened -= q.poll();
        }
    }
    return opened == 0;
}

Wiggle Sort

1
2
3
4
5
6
7
8
9
10
11
12
13
public void wiggleSort(int[] nums) {
    for (int i = 0; i < nums.length - 1; i++) {
        if ((i % 2 == 0) == (nums[i] > nums[i + 1])) {
            swap(nums, i, i + 1);
        }
    }
}

private void swap(int[] A, int i, int j) {
    int tmp = A[i];
    A[i] = A[j];
    A[j] = tmp;
}

Minimum Factorization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int smallestFactorization(int a) {
    if (a < 10) {
        return a;
    }

    long b = 0, t = 1;
    for (int i = 9; i >= 2; i--) {
        while (a % i == 0) {
            a /= i;
            b += t * i;
            t *= 10;
        }
    }
    return a == 1 && b <= Integer.MAX_VALUE ? (int)b : 0;
}

Put Boxes Into the Warehouse I

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int maxBoxesInWarehouse(int[] boxes, int[] warehouse) {
    Arrays.sort(boxes);

    int i = boxes.length - 1, j = 0, count = 0;
    while (i >= 0 && j < warehouse.length) {
        // all following boxes will fit in warehouse[j]
        if (boxes[i] <= warehouse[j]) {
            j++;
            count++;
        }
        i--;
    }
    return count;
}

Maximum Binary String After Change

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public String maximumBinaryString(String binary) {
    // we can always make the string contain at most one '0'
    int ones = 0, zeros = 0, n = binary.length();
    StringBuilder sb = new StringBuilder("1".repeat(n));
    for (int i = 0; i < n; i++) {
        if (binary.charAt(i) == '0') {
            zeros++;
        } else if (zeros == 0) {
            // leading '1's
            ones++;
        }
    }
    if (ones < n) {
        sb.setCharAt(ones + zeros - 1, '0');
    }
    return sb.toString();
}

Maximum Number of Ones

2D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int maximumNumberOfOnes(int width, int height, int sideLength, int maxOnes) {
    // greedy - translation of a single sub-matrix
    // considers all positions in the sub-matrix
    // counts their occurrences in M
    int[] count = new int[sideLength * sideLength];
    for (int i = 0; i < sideLength; i++) {
        for (int j = 0; j < sideLength; j++) {
            // Math.ceil((width - i) / (double)sideLength)
            count[i * sideLength + j] += ((width - i - 1) / sideLength + 1) * ((height - j - 1) / sideLength + 1);
        }
    }

    // sorts the positions by occurrences
    Arrays.sort(count);

    // assigns ones to positions with more occurrences
    int sum = 0;
    for (int i = count.length - 1; i > count.length - 1 - maxOnes; i--) {
        sum += count[i];
    }
    return sum;
}

Strong Password Checker

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public int strongPasswordChecker(String s) {
    int missingTypes = 3;
    for (char c : s.toCharArray()) {
        if (Character.isUpperCase(c)) {
            missingTypes--;
            break;
        }
    }
    for (char c : s.toCharArray()) {
        if (Character.isLowerCase(c)) {
            missingTypes--;
            break;
        }
    }
    for (char c : s.toCharArray()) {
        if (Character.isDigit(c)) {
            missingTypes--;
            break;
        }
    }

    int n = s.length();
    if (n < 6) {
        // (6 - n): number of chars to be inserted
        // if 6 - n < missingTypes, we will need to replace some chars, too
        // the length of a repeating char seq <= 5
        // so one insertion can break it
        return Math.max(6 - n, missingTypes);
    }

    // mod0: count of repeating char seq those length % 3 == 0
    // mod1: count of repeating char seq those length % 3 == 1
    int i = 2, mod0 = 0, mod1 = 0, replacements = 0;
    while (i < n) {
        // three repeating chars in a row
        if (s.charAt(i) == s.charAt(i - 1) && s.charAt(i) == s.charAt(i - 2)) {
            // finds the length of the sequence
            int len = 2;
            while (i < n && s.charAt(i) == s.charAt(i - 1)) {
                len++;
                i++;
            }

            // always replace the third char
            replacements += len / 3;

            if (len % 3 == 0) {
                mod0++;
            } else if (len % 3 == 1) {
                mod1++;
            }
        } else {
            i++;
        }
    }

    if (n <= 20) {
        // minimum replacement
        return Math.max(replacements, missingTypes);
    }

    // when n > 20, the idea is to prefer deletions over replacements
    // (length / 3) replacements = (length / 3 - 1) replacements + (length % 3 + 1) deletions
    int deletes = n - 20;
    // length % 3 == 0, one replacement => one deletion
    replacements -= Math.min(mod0, deletes);
    // length % 3 == 1, one replacement => two deletions
    // Math.max(deletes - mod0, 0) is the remaining deletes after the above action
    replacements -= Math.min(Math.max(deletes - mod0, 0), mod1 * 2) / 2;
    // length % 3 == 2, one replacement => three deletions
    // Math.max(deletes - mod0 - mod1 * 2, 0) is the remaining deletes after the above actions
    replacements -= Math.max(deletes - mod0 - mod1 * 2, 0) / 3;
    return deletes + Math.max(replacements, missingTypes);
}

Minimum Adjacent Swaps to Reach the Kth Smallest Number

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public int getMinSwaps(String num, int k) {
    char[] chars = num.toCharArray();
    for (int i = 0; i < k; i++) {
        nextPermutation(chars);
    }

    int n = num.length(), count = 0;
    for (int i = 0; i < n; i++) {
        if (num.charAt(i) != chars[i]) {
            for (int j = i + 1; j < n; j++) {
                swap(chars, i, j);
                count++;
                if (num.charAt(i) == chars[i]) {
                    break;
                }
            }
        }
    }
    return count;
}

Minimum Initial Energy to Finish Tasks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int minimumEffort(int[][] tasks) {
    // sorts to maximize running energy
    Arrays.sort(tasks, Comparator.comparingInt(a -> a[0] - a[1]));

    int initial = 0, energy = 0;
    for (int[] t : tasks) {
        if (energy < t[1]) {
            initial += t[1] - energy;
            energy = t[1] - t[0];
        } else {
            energy -= t[0];
        }
    }
    return initial;
}

Minimum Number of Days to Eat N Oranges

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Map<Integer, Integer> memo = new HashMap<>();

public int minDays(int n) {
    if (n < 2) {
        return n;
    }

    if (memo.containsKey(n)) {
        return memo.get(n);
    }

    int days = 1 + Math.min(n % 2 + minDays(n / 2), n % 3 + minDays(n / 3));
    memo.put(n, days);

    return days;
}

Minimum Number of People to Teach

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
public int minimumTeachings(int n, int[][] languages, int[][] friendships) {
    int m = languages.length;
    // language set for each user
    Set<Integer>[] ls = new Set[m + 1];
    for (int i = 1; i <= m; i++) {
        ls[i] = new HashSet<>();
    }
    for (int i = 0; i < m; i++) {
        ls[i + 1] = Arrays.stream(languages[i]).boxed().collect(Collectors.toSet());
    }

    // finds the set of people who can't communicate with at least one friend
    Set<Integer> set = new HashSet<>();
    for (int[] f : friendships) {
        if (Collections.disjoint(ls[f[0]], ls[f[1]])) {
            set.add(f[0]);
            set.add(f[1]);
        }
    }

    // finds the count of users in the set that speak each language
    int[] userCount = new int[n + 1];
    for (int u : set) {
        for (int l : languages[u - 1]) {
            userCount[l]++;
        }
    }

    // finds the language that most people know
    return set.size() - Arrays.stream(userCount).max().getAsInt();
}

Maximum Score From Removing Substrings

1
2
3
4
5
6
public int maximumGain(String s, int x, int y) {
    StringBuilder sb = new StringBuilder(s);
    // greedily removes the pattern with greater points, then remove the other pattern
    // intuition: the total number of deletions is a fixed value, no matter which pattern is deleted first
    return x > y ? remove(sb, "ab", x) + remove(sb, "ba", y) : remove(sb, "ba", y) + remove(sb, "ab", x);
}

Earliest Possible Day of Full Bloom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int earliestFullBloom(int[] plantTime, int[] growTime) {
    int n = plantTime.length;
    Integer[] index = new Integer[n];
    for (int i = 0; i < n; i++) {
        index[i] = i;
    }

    // switching between seeds just delays the starting grow time for (at least one of) the seeds
    // so we never switch
    // plants seeds with greater growTime first
    Arrays.sort(index, Comparator.comparingInt(i -> -growTime[i]));

    int start = 0, end = 0;
    for (int i : index) {
        end = Math.max(end, start + plantTime[i] + growTime[i]);
        start += plantTime[i];
    }
    return end;
}

Make Array Non-decreasing or Non-increasing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int convertArray(int[] nums) {
    return Math.min(helper(nums, 1), helper(nums, -1));
}

private int helper(int[] nums, int sign) {
    Queue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder());
    int cost = 0;
    for (int i = sign > 0 ? 0 : nums.length - 1; i < nums.length && i >= 0; i += sign) {
        if (!pq.isEmpty() && pq.peek() > nums[i]) {
            cost += pq.poll() - nums[i];
            pq.offer(nums[i]);
        }
        pq.offer(nums[i]);
    }
    return cost;
}

Minimum Operations to Form Subsequence With Target Sum

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
int minOperations(vector<int>& nums, int target) {
    long long sum = accumulate(nums.begin(), nums.end(), 0l);
    if (sum < target) {
        return -1;
    }

    sort(nums.begin(), nums.end());
    int ops = 0;
    while (target > 0) {
        int mx = nums.back();
        nums.pop_back();
        if (sum - mx >= target) {
            // All the elements other than max can sum to target
            // so it's safe to skip mx directly
            sum -= mx;
        } else if (mx <= target) {
            // Picks the current max
            sum -= mx;
            target -= mx;
        } else {
            // sum - mx < target && mx > target
            // sum < mx + target < 2 * mx
            // mx > sum / 2
            nums.push_back(mx / 2);
            nums.push_back(mx / 2);
            ops++;
        }
    }
    return ops;
}
This post is licensed under CC BY 4.0 by the author.