You are given a list of strings of the same length words
and a string target
. Your task is to form target
using the given words
under the following rules:
target
should be formed from left to right.i
th character (0-indexed) of target
, you can choose the k
th character of the j
th string in words
if target[i] = words[j][k]
.k
th character of the j
th string of words
, you can no longer use the x
th character of any string in words
where x <= k
. In other words, all characters to the left of or at index k
become unusuable for every string.target
.Notice that you can use multiple characters from the same string in words
provided the conditions above are met.
Return the number of ways to form target
from words
. Since the answer may be too large, return it modulo 10^9 + 7
.
Example 1:
`words = [
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach involves trying every possible combination of characters from the given word list to build the target string. We explore all paths, one character at a time, to see if we can construct the target. We count the number of successful paths.
Here's how the algorithm would work step-by-step:
def number_of_ways_brute_force(
word_list, target_string
):
number_of_ways = 0
def backtrack(
target_index,
current_path,
):
nonlocal number_of_ways
# If we've built the entire target string,
# increment the count
if target_index == len(target_string):
number_of_ways += 1
return
for word in word_list:
# Iterate through each column of the word
for column_index in range(len(word)):
# Check if the character at
# current column matches
if (
column_index < len(word)
and target_string[target_index] == word[column_index]
):
# Explore the next character
backtrack(
target_index + 1,
current_path + [word[column_index]],
)
backtrack(0, [])
return number_of_ways
We want to count how many ways we can build our target string by picking characters from the given dictionary words. The clever trick is to use a system where we remember results we've already calculated, so we don't repeat the same work over and over. This makes the whole process much faster.
Here's how the algorithm would work step-by-step:
def number_of_ways_to_form_target(
dictionary_words, target_string):
word_length = len(dictionary_words[0])
target_length = len(target_string)
modulo = 10**9 + 7
# Stores char frequencies at each index.
character_counts = [([0] * 26) for _ in range(word_length)]
for word in dictionary_words:
for index, char in enumerate(word):
character_counts[index][ord(char) - ord('a')] += 1
memo = {}
def calculate_ways(index_in_target, index_in_word):
if index_in_target == target_length:
return 1
if index_in_word == word_length:
return 0
if (index_in_target, index_in_word) in memo:
return memo[(index_in_target, index_in_word)]
ways = calculate_ways(index_in_target,
index_in_word + 1) % modulo
target_char_index =
ord(target_string[index_in_target]) - ord('a')
#If character can form target.
if character_counts[index_in_word][target_char_index] > 0:
ways += (calculate_ways(index_in_target + 1,
index_in_word + 1) *
character_counts[index_in_word][target_char_index])
ways %= modulo
memo[(index_in_target, index_in_word)] = ways
return ways
# Initiate recursive calls.
result = calculate_ways(0, 0)
return result
Case | How to Handle |
---|---|
Empty words array | If the words array is empty, there are zero ways to form the target string; return 0. |
Empty target string | If the target string is empty, there is one way to form it (by selecting nothing); return 1. |
Target string longer than the longest word multiplied by number of words | If the target string is longer than the maximum possible length that can be formed, return 0. |
Words array contains empty strings | Empty strings in words should be ignored or handled as characters that cannot contribute to forming the target. |
Characters in target string not present in any of the words | If a character in the target string is not present in any of the given words at appropriate indices, the result should be 0. |
Integer overflow when calculating number of ways | Use modulo operation throughout the calculation to prevent integer overflow (e.g., modulo 10^9 + 7). |
Words with varying lengths | Handle words of different lengths by ensuring only valid indices are accessed when matching characters. |
Very large input sizes for words and target that can cause stack overflow during recursion | Use dynamic programming (tabulation) instead of recursion to avoid stack overflow errors. |