LeetCode-in-Java

3753. Total Waviness of Numbers in Range II

Hard

You are given two integers num1 and num2 representing an inclusive range [num1, num2].

The waviness of a number is defined as the total count of its peaks and valleys:

Return the total sum of waviness for all numbers in the range [num1, num2].

Example 1:

Input: num1 = 120, num2 = 130

Output: 3

Explanation:

In the range [120, 130]:

Thus, total waviness is 1 + 1 + 1 = 3.

Example 2:

Input: num1 = 198, num2 = 202

Output: 3

Explanation:

In the range [198, 202]:

Thus, total waviness is 1 + 1 + 1 = 3.

Example 3:

Input: num1 = 4848, num2 = 4848

Output: 2

Explanation:

Number 4848: the second digit 8 is a peak, and the third digit 4 is a valley, giving a waviness of 2.

Constraints:

Solution

public class Solution {
    private static class Pair {
        long count;
        long sum;

        Pair(long count, long sum) {
            this.count = count;
            this.sum = sum;
        }
    }

    private char[] digits;
    private Pair[][][][][] memo;
    private boolean[][][][][] seen;

    public long totalWaviness(long num1, long num2) {
        return solve(num2) - solve(num1 - 1);
    }

    private long solve(long x) {
        if (x <= 0) {
            return 0;
        }
        digits = Long.toString(x).toCharArray();
        int n = digits.length;
        memo = new Pair[n][2][2][11][11];
        seen = new boolean[n][2][2][11][11];
        return dfs(0, 1, 0, 10, 10).sum;
    }

    private Pair dfs(int pos, int tight, int started, int prev2, int prev1) {
        if (pos == digits.length) {
            return new Pair(1, 0);
        }
        if (seen[pos][tight][started][prev2][prev1]) {
            return memo[pos][tight][started][prev2][prev1];
        }
        seen[pos][tight][started][prev2][prev1] = true;
        int limit = tight == 1 ? digits[pos] - '0' : 9;
        long totalCount = 0;
        long totalSum = 0;
        for (int d = 0; d <= limit; d++) {
            int nextTight = (tight == 1 && d == limit) ? 1 : 0;
            if (started == 0 && d == 0) {
                // still leading zeros, number not started
                Pair nxt = dfs(pos + 1, nextTight, 0, 10, 10);
                totalCount += nxt.count;
                totalSum += nxt.sum;
            } else if (started == 0) {
                // first real digit
                Pair nxt = dfs(pos + 1, nextTight, 1, 10, d);
                totalCount += nxt.count;
                totalSum += nxt.sum;
            } else if (prev2 == 10) {
                // second real digit
                Pair nxt = dfs(pos + 1, nextTight, 1, prev1, d);
                totalCount += nxt.count;
                totalSum += nxt.sum;
            } else {
                // now prev1 is an interior candidate, decide if it is peak/valley
                int add = 0;
                if ((prev1 > prev2 && prev1 > d) || (prev1 < prev2 && prev1 < d)) {
                    add = 1;
                }
                Pair nxt = dfs(pos + 1, nextTight, 1, prev1, d);
                totalCount += nxt.count;
                totalSum += nxt.sum + add * nxt.count;
            }
        }
        memo[pos][tight][started][prev2][prev1] = new Pair(totalCount, totalSum);
        return memo[pos][tight][started][prev2][prev1];
    }
}