Before attempting this problem, you should be comfortable with:
We build the target string character by character. For each target character, we can pick it from any word at the current column position, then move to the next column. We can also skip columns without picking anything. The constraint is that once we use a column, we cannot go back to previous columns.
dfs(i, k) where i is the current target index and k is the current column index in the words.i == n (target length), we formed the entire target, return 1.k == m (word length), we ran out of columns, return 0.k (call dfs(i, k + 1)).word[k] matches target[i], add dfs(i + 1, k + 1) to res.10^9 + 7.class Solution:
def numWays(self, words: List[str], target: str) -> int:
mod = 10**9 + 7
n, m = len(target), len(words[0])
def dfs(i, k):
if i == n:
return 1
if k == m:
return 0
res = dfs(i, k + 1)
for w in words:
if w[k] != target[i]:
continue
res = (res + dfs(i + 1, k + 1)) % mod
return res
return dfs(0, 0)Where is the number of words, is the length of each word, and is the length of the string.
The naive recursion is slow because we check every word at each step. We can precompute how many times each character appears at each column position. Then instead of iterating through all words, we simply multiply by the count of matching characters.
cnt[k][c] storing how many words have character c at column k.dfs(i, k) with memoization:k by adding dfs(i, k + 1).k by multiplying cnt[k][target[i]] with dfs(i + 1, k + 1).dfs(0, 0) modulo 10^9 + 7.class Solution:
def numWays(self, words: List[str], target: str) -> int:
mod = 10**9 + 7
cnt = defaultdict(int) # (index, char) --> count among all words
for w in words:
for i, c in enumerate(w):
cnt[(i, c)] += 1
dp = {}
def dfs(i, k):
if i == len(target):
return 1
if k == len(words[0]):
return 0
if (i, k) in dp:
return dp[(i, k)]
c = target[i]
dp[(i, k)] = dfs(i, k + 1) # skip k position
dp[(i, k)] += cnt[(k, c)] * dfs(i + 1, k + 1)
dp[(i, k)] %= mod
return dp[(i, k)]
return dfs(0, 0)Where is the number of words, is the length of each word, and is the length of the string.
We can convert the memoized solution to a bottom-up DP. We fill a 2D table where dp[i][k] represents the number of ways to form target[i:] using columns k to m-1.
(n+1) x (m+1) initialized to 0.dp[n][m] = 1 (base case: empty target from the end is valid).(i, k), set dp[i][k] = dp[i][k+1] (skip column).i < n, add cnt[k][target[i]] * dp[i+1][k+1] (use column).dp[0][0] modulo 10^9 + 7.class Solution:
def numWays(self, words: List[str], target: str) -> int:
MOD = 10**9 + 7
n, m = len(target), len(words[0])
cnt = [[0] * 26 for _ in range(m)]
for word in words:
for i, c in enumerate(word):
cnt[i][ord(c) - ord('a')] += 1
dp = [[0] * (m + 1) for _ in range(n + 1)]
dp[n][m] = 1
for i in range(n, -1, -1):
for k in range(m - 1, -1, -1):
dp[i][k] = dp[i][k + 1]
if i < n:
c = ord(target[i]) - ord('a')
dp[i][k] = (dp[i][k] + cnt[k][c] * dp[i + 1][k + 1]) % MOD
return dp[0][0]Where is the number of words, is the length of each word, and is the length of the string.
Since we only need the previous row of the DP table to compute the current row, we can reduce space by using a single 1D array.
m + 1.i = n down to 0, and for each row, iterate through columns in reverse.dp[0] modulo 10^9 + 7.class Solution:
def numWays(self, words: List[str], target: str) -> int:
MOD = 10**9 + 7
n, m = len(target), len(words[0])
cnt = [[0] * 26 for _ in range(m)]
for word in words:
for i, c in enumerate(word):
cnt[i][ord(c) - ord('a')] += 1
dp = [0] * (m + 1)
dp[m] = 1
for i in range(n, -1, -1):
nxt = 1 if i == n - 1 else 0
for k in range(m - 1, -1, -1):
cur = dp[k]
dp[k] = dp[k + 1]
if i < n:
c = ord(target[i]) - ord('a')
dp[k] = (dp[k] + cnt[k][c] * nxt) % MOD
nxt = cur
dp[m] = 0
return dp[0]Where is the number of words, is the length of each word, and is the length of the string.
A common mistake is iterating through all words at each step to count matching characters. This leads to TLE because the time complexity becomes per state transition. Instead, precompute a frequency table cnt[k][c] storing how many words have character c at column k before starting the DP.
When computing cnt[k][c] * dp[i+1][k+1], both values can be large. In languages like Java and C++, multiplying two int values can overflow before the modulo operation is applied. Always cast to long or long long before multiplication: (long) cnt[k][c] * dp[i+1][k+1] % MOD.
In bottom-up DP, the base case placement matters. Setting dp[n][k] = 1 for all k is incorrect because only dp[n][m] = 1 represents a valid completion (target fully matched at the end). The recurrence dp[i][k] = dp[i][k+1] propagates this value correctly, but initializing all dp[n][k] = 1 would overcount.
The DP state involves two indices: i for the target position and k for the column in words. A common error is swapping their roles or forgetting that k can skip ahead (we can skip columns) while i must advance exactly by 1 each time we pick a character. The column index k must always be at least as large as the target index i.
If the target string is longer than the word length (n > m), it is impossible to form the target since we need at least n columns. The algorithm should return 0 in this case. While the recursion handles this naturally (running out of columns before finishing target), explicitly checking can prevent unnecessary computation.