Skip to content

3333. Find the Original Typed String II

Description

Alice is attempting to type a specific string on her computer. However, she tends to be clumsy and may press a key for too long, resulting in a character being typed multiple times.

You are given a string word, which represents the final output displayed on Alice's screen. You are also given a positive integer k.

Return the total number of possible original strings that Alice might have intended to type, if she was trying to type a string of size at least k.

Since the answer may be very large, return it modulo 109 + 7.

 

Example 1:

Input: word = "aabbccdd", k = 7

Output: 5

Explanation:

The possible strings are: "aabbccdd", "aabbccd", "aabbcdd", "aabccdd", and "abbccdd".

Example 2:

Input: word = "aabbccdd", k = 8

Output: 1

Explanation:

The only possible string is "aabbccdd".

Example 3:

Input: word = "aaabbb", k = 3

Output: 8

 

Constraints:

  • 1 <= word.length <= 5 * 105
  • word consists only of lowercase English letters.
  • 1 <= k <= 2000

Solutions

Solution 1: Dynamic Programming + Prefix Sum

For the constraint that the length is at least \(k\), we can split it into two subproblems:

  • Without length restriction, for each group of consecutive identical characters, we can choose any number from \(1\) to the length of the group. Let the number of ways be \(a\).
  • For length less than \(k\), let the number of ways be \(b\).

Thus, the final answer is \(a - b\).

We can group consecutive identical characters in the string \(\textit{word}\). Since at least one character must be chosen from each group, if a group has more than \(0\) remaining selectable characters, we add it to an array \(\textit{nums}\). After initially selecting one character from each group, we update the remaining required character count \(k\).

If \(k < 1\), it means that after selecting one character from each group, the requirement of length at least \(k\) is already satisfied, so the answer is \(a\).

Otherwise, we need to calculate the value of \(b\). We use a 2D array \(\textit{f}\), where \(\textit{f}[i][j]\) represents the number of ways to select \(j\) characters from the first \(i\) groups. Initially, \(\textit{f}[0][0] = 1\), meaning there is \(1\) way to select \(0\) characters from \(0\) groups. Then \(b = \sum_{j=0}^{k-1} \text{f}[m][j]\), where \(m\) is the length of \(\textit{nums}\). The answer is \(a - b\).

Consider the transition equation for \(\textit{f}[i][j]\). For the \(i\)-th group of characters, suppose its remaining length is \(x\). For each \(j\), we can enumerate the number of characters \(l\) chosen from this group, where \(l \in [0, \min(x, j)]\). Then, \(\textit{f}[i][j]\) can be transferred from \(\textit{f}[i-1][j-l]\). We can use prefix sums to optimize this transition.

The time complexity is \(O(n + k^2)\), and the space complexity is \(O(k^2)\), where \(n\) is the length of the string \(\textit{word}\).

 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
class Solution:
    def possibleStringCount(self, word: str, k: int) -> int:
        mod = 10**9 + 7
        nums = []
        ans = 1
        cur = 0
        for i, c in enumerate(word):
            cur += 1
            if i == len(word) - 1 or c != word[i + 1]:
                if cur > 1:
                    if k > 0:
                        nums.append(cur - 1)
                    ans = ans * cur % mod
                cur = 0
                k -= 1
        if k < 1:
            return ans
        m = len(nums)
        f = [[0] * k for _ in range(m + 1)]
        f[0][0] = 1
        for i, x in enumerate(nums, 1):
            s = list(accumulate(f[i - 1], initial=0))
            for j in range(k):
                f[i][j] = (s[j + 1] - s[j - min(x, j)] + mod) % mod
        return (ans - sum(f[m][j] for j in range(k))) % mod
 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
class Solution {
    public int possibleStringCount(String word, int k) {
        final int mod = (int) 1e9 + 7;
        List<Integer> nums = new ArrayList<>();
        long ans = 1;
        int cur = 0;
        int n = word.length();

        for (int i = 0; i < n; i++) {
            cur++;
            if (i == n - 1 || word.charAt(i) != word.charAt(i + 1)) {
                if (cur > 1) {
                    if (k > 0) {
                        nums.add(cur - 1);
                    }
                    ans = ans * cur % mod;
                }
                cur = 0;
                k--;
            }
        }

        if (k < 1) {
            return (int) ans;
        }

        int m = nums.size();
        int[][] f = new int[m + 1][k];
        f[0][0] = 1;

        for (int i = 1; i <= m; i++) {
            int x = nums.get(i - 1);
            long[] s = new long[k + 1];
            for (int j = 0; j < k; j++) {
                s[j + 1] = (s[j] + f[i - 1][j]) % mod;
            }
            for (int j = 0; j < k; j++) {
                int l = Math.max(0, j - x);
                f[i][j] = (int) ((s[j + 1] - s[l] + mod) % mod);
            }
        }

        long sum = 0;
        for (int j = 0; j < k; j++) {
            sum = (sum + f[m][j]) % mod;
        }

        return (int) ((ans - sum + mod) % mod);
    }
}
 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:
    int possibleStringCount(string word, int k) {
        const int mod = 1e9 + 7;
        vector<int> nums;
        long long ans = 1;
        int cur = 0;
        int n = word.size();

        for (int i = 0; i < n; ++i) {
            cur++;
            if (i == n - 1 || word[i] != word[i + 1]) {
                if (cur > 1) {
                    if (k > 0) {
                        nums.push_back(cur - 1);
                    }
                    ans = ans * cur % mod;
                }
                cur = 0;
                k--;
            }
        }

        if (k < 1) {
            return ans;
        }

        int m = nums.size();
        vector<vector<int>> f(m + 1, vector<int>(k, 0));
        f[0][0] = 1;

        for (int i = 1; i <= m; ++i) {
            int x = nums[i - 1];
            vector<long long> s(k + 1, 0);
            for (int j = 0; j < k; ++j) {
                s[j + 1] = (s[j] + f[i - 1][j]) % mod;
            }
            for (int j = 0; j < k; ++j) {
                int l = max(0, j - x);
                f[i][j] = (s[j + 1] - s[l] + mod) % mod;
            }
        }

        long long sum = 0;
        for (int j = 0; j < k; ++j) {
            sum = (sum + f[m][j]) % mod;
        }

        return (ans - sum + mod) % mod;
    }
};
 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
func possibleStringCount(word string, k int) int {
    const mod = 1_000_000_007
    nums := []int{}
    ans := 1
    cur := 0
    n := len(word)

    for i := 0; i < n; i++ {
        cur++
        if i == n-1 || word[i] != word[i+1] {
            if cur > 1 {
                if k > 0 {
                    nums = append(nums, cur-1)
                }
                ans = ans * cur % mod
            }
            cur = 0
            k--
        }
    }

    if k < 1 {
        return ans
    }

    m := len(nums)
    f := make([][]int, m+1)
    for i := range f {
        f[i] = make([]int, k)
    }
    f[0][0] = 1

    for i := 1; i <= m; i++ {
        x := nums[i-1]
        s := make([]int, k+1)
        for j := 0; j < k; j++ {
            s[j+1] = (s[j] + f[i-1][j]) % mod
        }
        for j := 0; j < k; j++ {
            l := j - x
            if l < 0 {
                l = 0
            }
            f[i][j] = (s[j+1] - s[l] + mod) % mod
        }
    }

    sum := 0
    for j := 0; j < k; j++ {
        sum = (sum + f[m][j]) % mod
    }

    return (ans - sum + mod) % mod
}
 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
function possibleStringCount(word: string, k: number): number {
    const mod = 1_000_000_007;
    const nums: number[] = [];
    let ans = 1;
    let cur = 0;
    const n = word.length;

    for (let i = 0; i < n; i++) {
        cur++;
        if (i === n - 1 || word[i] !== word[i + 1]) {
            if (cur > 1) {
                if (k > 0) {
                    nums.push(cur - 1);
                }
                ans = (ans * cur) % mod;
            }
            cur = 0;
            k--;
        }
    }

    if (k < 1) {
        return ans;
    }

    const m = nums.length;
    const f: number[][] = Array.from({ length: m + 1 }, () => Array(k).fill(0));
    f[0][0] = 1;

    for (let i = 1; i <= m; i++) {
        const x = nums[i - 1];
        const s: number[] = Array(k + 1).fill(0);
        for (let j = 0; j < k; j++) {
            s[j + 1] = (s[j] + f[i - 1][j]) % mod;
        }
        for (let j = 0; j < k; j++) {
            const l = Math.max(0, j - x);
            f[i][j] = (s[j + 1] - s[l] + mod) % mod;
        }
    }

    let sum = 0;
    for (let j = 0; j < k; j++) {
        sum = (sum + f[m][j]) % mod;
    }

    return (ans - sum + mod) % mod;
}

Comments

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy