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 109 + 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". Then there are 25 good strings starting with 'c': "ca","cc","cd",...,"cz" and finally there is one good string starting with 'd': "da".
Example 2:
Input: n = 8, s1 = "leetcode", s2 = "leetgoes", evil = "leet" Output: 0 Explanation: All strings greater than or equal to s1 and smaller than or equal to s2 start with the prefix "leet", therefore, there is not any good string.
Example 3:
Input: n = 2, s1 = "gx", s2 = "gz", evil = "x" Output: 2
Constraints:
s1.length == n
s2.length == n
s1 <= s2
1 <= n <= 500
1 <= evil.length <= 50
This problem requires finding the number of strings of length n
that are lexicographically between s1
and s2
(inclusive) and do not contain the substring evil
. A naive approach would be to generate all strings and check each one, but this is computationally infeasible for larger n
. A more efficient solution uses dynamic programming.
Approach:
The core idea is to build a dynamic programming solution that avoids explicitly generating all possible strings. We'll use the following concepts:
KMP (Knuth-Morris-Pratt) Algorithm: We need an efficient way to check if a string contains evil
as a substring. The KMP algorithm is perfect for this, providing O(m) time complexity (where m is the length of evil
).
Dynamic Programming: We'll build a DP table to store the counts of good strings. The state will represent the current length of the string being built and the current suffix (to check for evil
).
States and Transitions:
Let dp[i][j]
represent the count of good strings of length i
ending with the suffix j
of evil
. j
can range from 0 (no matching suffix of evil
) to len(evil) -1
(full match of evil
found).
dp[0][0] = 1
(empty string is a valid starting point).c
in 'a' to 'z', we can extend a string of length i-1
to a string of length i
. We need to check:
c
lead to the creation of substring evil
? This is determined by the KMP failure function (next array).[s1, s2]
range?Algorithm:
Preprocessing (KMP): Calculate the KMP next
array for the evil
string. This array helps efficiently find evil
substrings.
DP Table Initialization: Initialize the dp
table to 0. Set dp[0][0] = 1
.
Iteration: Iterate through string lengths (i
from 1 to n
). For each length, iterate through possible suffix matches (j
from 0 to len(evil) - 1
). Iterate through all possible characters ('a' to 'z').
Character Addition: For each character c
:
next_j
using the KMP next
array.c
keeps the string within the bounds [s1, s2].dp[i][next_j] += dp[i - 1][j]
Final Result: The total count of good strings will be the sum of all dp[n][j]
where j
is 0 (i.e., no evil
suffix).
Code (Python):
def kmp_preprocess(evil):
next = [0] * len(evil)
j = 0
for i in range(1, len(evil)):
while j > 0 and evil[i] != evil[j]:
j = next[j - 1]
if evil[i] == evil[j]:
j += 1
next[i] = j
return next
def findGoodStrings(n, s1, s2, evil):
mod = 10**9 + 7
next = kmp_preprocess(evil)
dp = [[0] * len(evil) for _ in range(n + 1)]
dp[0][0] = 1
for i in range(1, n + 1):
for j in range(len(evil)):
for c in range(ord('a'), ord('z') + 1):
char = chr(c)
next_j = j
while next_j > 0 and char != evil[next_j]:
next_j = next[next_j - 1]
if char == evil[next_j]:
next_j += 1
cur_str = s1[:i-1] + char
if i == n:
cur_str = s1[:i-1] + char
if (cur_str >= s1 and cur_str <= s2 and next_j == 0 ):
dp[i][next_j] = (dp[i][next_j] + dp[i - 1][j]) % mod
elif next_j == 0 and cur_str >= s1 and cur_str <= s2:
dp[i][next_j] = (dp[i][next_j] + dp[i - 1][j]) % mod
ans = 0
for j in range(len(evil)):
ans = (ans + dp[n][j]) % mod
return ans
Time Complexity: O(n * 26 * len(evil)) which simplifies to O(n * len(evil)). The dominating factor is the DP iteration.
Space Complexity: O(n * len(evil)) for the DP table. The KMP next
array is O(len(evil)).
This detailed explanation, along with the Python code, provides a complete solution for the "Find All Good Strings" problem. Adaptations to other languages (Java, C++, Go) would follow a similar structure, primarily altering syntax and potentially utilizing built-in KMP implementations if available.