Find All Good Strings - Complete Solution Guide
Find All Good Strings is LeetCode problem 1397, a Hard level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
Given the strings s1 and s2 of size n and the string evil , return the number of good strings . A good string has size n , it is alphabetically greater than or equal to s1 , it is alphabetically smaller than or equal to s2 , and it does not contain the string evil as a substring. Since the answer can be a huge number, return this modulo 10 9 + 7 . Example 1: Input: n = 2, s1 = "aa", s2 = "da", evil = "b" Output: 51 Explanation: There are 25 good strings starting with 'a': "aa","ac","ad",...,"az"
Detailed Explanation
The problem requires you to find the number of "good" strings of length 'n' that satisfy the following conditions: 1) the string must be lexicographically greater than or equal to 's1', 2) the string must be lexicographically less than or equal to 's2', and 3) the string must not contain 'evil' as a substring. The result needs to be returned modulo 10^9 + 7 to prevent integer overflow. 's1' and 's2' are of length 'n'.
Solution Approach
The solution employs a dynamic programming approach with memoization to count the number of good strings. A helper function `dp(idx, tight1, tight2, evil_match_len)` recursively calculates the number of valid strings starting from index `idx`, considering whether it's tightly bound by `s1` and `s2` (`tight1`, `tight2`) and how much of `evil` has been matched (`evil_match_len`). The KMP algorithm is used to build a transition table that determines the next `evil_match_len` given a character and the current `evil_match_len`. The base cases are when a full match of 'evil' is found (return 0) or a valid string of length n is formed (return 1).
Step-by-Step Algorithm
- Step 1: Calculate the LPS (Longest Proper Prefix which is also a Suffix) array for the 'evil' string using the KMP algorithm. This is a pre-processing step.
- Step 2: Create a transition table that maps the current match length of 'evil' and the next character to the new match length. This table is derived from the LPS array.
- Step 3: Define a recursive function `dp(idx, tight1, tight2, evil_match_len)` that takes the current index in the string being built, tight constraints, and the length of the current match with 'evil' as input.
- Step 4: Inside the `dp` function, check the base cases: if `evil_match_len == m` (the length of 'evil'), return 0 because the string contains 'evil' as a substring and isn't valid. If `idx == n` (the length of the string being built is 'n'), return 1, because a valid string has been formed.
- Step 5: Iterate through all possible characters for the current position 'idx', considering the tight constraints imposed by 's1' and 's2'.
- Step 6: For each character, update the tight constraints and the length of the match with 'evil' using the precomputed transition table.
- Step 7: Recursively call the `dp` function with the updated parameters and accumulate the results, taking the modulo at each step to prevent overflow.
- Step 8: Memoize the result of each `dp` call to avoid recalculating the same state. This drastically improves performance.
- Step 9: Call the `dp` function starting from index 0 with initial tight constraints set to true and `evil_match_len` set to 0.
Key Insights
- Insight 1: Dynamic programming is essential due to overlapping subproblems when counting valid strings based on prefixes and substring avoidance. Memoization helps avoid redundant calculations.
- Insight 2: KMP algorithm is needed to efficiently precompute the longest proper prefix that is also a suffix (LPS) for the 'evil' string. This allows determining how much of 'evil' has been matched so far as we build our good strings.
- Insight 3: Keeping track of whether the current string being built is lexicographically constrained by 's1' and 's2' is crucial. These constraints are handled with 'tight' boolean variables to manage the valid character ranges at each position.
Complexity Analysis
Time Complexity: O(n*m)
Space Complexity: O(n*m)
Topics
This problem involves: String, Dynamic Programming, String Matching.
Companies
Asked at: Dunzo.