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:
targetshould be formed from left to right.- To form the
ithcharacter (0-indexed) oftarget, you can choose thekthcharacter of thejthstring inwordsiftarget[i] = words[j][k]. - Once you use the
kthcharacter of thejthstring ofwords, you can no longer use thexthcharacter of any string inwordswherex <= k. In other words, all characters to the left of or at indexkbecome unusuable for every string. - Repeat the process until you form the 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 109 + 7.
Example 1:
Input: words = ["acca","bbbb","caca"], target = "aba"
Output: 6
Explanation: There are 6 ways to form target.
"aba" -> index 0 ("acca"), index 1 ("bbbb"), index 3 ("caca")
"aba" -> index 0 ("acca"), index 2 ("bbbb"), index 3 ("caca")
"aba" -> index 0 ("acca"), index 1 ("bbbb"), index 3 ("acca")
"aba" -> index 0 ("acca"), index 2 ("bbbb"), index 3 ("acca")
"aba" -> index 1 ("caca"), index 2 ("bbbb"), index 3 ("acca")
"aba" -> index 1 ("caca"), index 2 ("bbbb"), index 3 ("caca")
Example 2:
Input: words = ["abba","baab"], target = "bab"
Output: 4
Explanation: There are 4 ways to form target.
"bab" -> index 0 ("baab"), index 1 ("baab"), index 2 ("abba")
"bab" -> index 0 ("baab"), index 1 ("baab"), index 3 ("baab")
"bab" -> index 0 ("baab"), index 2 ("baab"), index 3 ("baab")
"bab" -> index 1 ("abba"), index 2 ("baab"), index 3 ("baab")
Constraints:
1 <= words.length <= 10001 <= words[i].length <= 1000- All strings in
wordshave the same length. 1 <= target.length <= 1000words[i]andtargetcontain only lowercase English letters.
Approach 01:
-
C++
-
Python
class Solution {
public:
int targetLength;
int wordLength;
const int MOD = 1e9+7;
int memo[1001][1001];
int solve(int targetIndex, int columnIndex, vector<vector<long long>>& charFrequency, string &target) {
if (targetIndex == targetLength) // Completed target formation
return 1;
if (columnIndex == wordLength) // Exhausted all columns
return 0;
if (memo[targetIndex][columnIndex] != -1) // Return memoized result
return memo[targetIndex][columnIndex];
// Option 1: Skip current column
int skipColumn = solve(targetIndex, columnIndex + 1, charFrequency, target) % MOD;
// Option 2: Use current column
int useColumn = (charFrequency[target[targetIndex] - 'a'][columnIndex] *
solve(targetIndex + 1, columnIndex + 1, charFrequency, target)) % MOD;
return memo[targetIndex][columnIndex] = (skipColumn + useColumn) % MOD;
}
int numWays(vector<string>& words, string target) {
wordLength = words[0].size();
targetLength = target.length();
// Build frequency table for each character at every column
vector<vector<long long>> charFrequency(26, vector<long long>(wordLength, 0));
for (int column = 0; column < wordLength; column++) {
for (string &word : words) {
charFrequency[word[column] - 'a'][column]++;
}
}
// Initialize memoization table
memset(memo, -1, sizeof(memo));
return solve(0, 0, charFrequency, target);
}
};
class Solution:
MOD = int(1e9+7)
def __init__(self):
self.memo = []
self.targetLength = 0
self.wordLength = 0
def solve(self, targetIndex, columnIndex, charFrequency, target):
if targetIndex == self.targetLength: # Completed target formation
return 1
if columnIndex == self.wordLength: # Exhausted all columns
return 0
if self.memo[targetIndex][columnIndex] != -1: # Return memoized result
return self.memo[targetIndex][columnIndex]
# Option 1: Skip current column
skipColumn = self.solve(targetIndex, columnIndex + 1, charFrequency, target) % self.MOD
# Option 2: Use current column
useColumn = (charFrequency[ord(target[targetIndex]) - ord('a')][columnIndex] *
self.solve(targetIndex + 1, columnIndex + 1, charFrequency, target)) % self.MOD
self.memo[targetIndex][columnIndex] = (skipColumn + useColumn) % self.MOD
return self.memo[targetIndex][columnIndex]
def numWays(self, words, target):
self.wordLength = len(words[0])
self.targetLength = len(target)
# Build frequency table for each character at every column
charFrequency = [[0] * self.wordLength for _ in range(26)]
for column in range(self.wordLength):
for word in words:
charFrequency[ord(word[column]) - ord('a')][column] += 1
# Initialize memoization table
self.memo = [[-1] * self.wordLength for _ in range(self.targetLength)]
return self.solve(0, 0, charFrequency, target)
Time Complexity
- Building Character Frequency Table:
- Iterating over each column of
words, for a total of \( O(w \cdot l) \), where \( w \) is the number of words and \( l \) is the length of each word.
- Iterating over each column of
- Recursive Function with Memoization:
- The function
solveis called with a maximum of \( O(targetLength \cdot wordLength) \) states due to the memoization table. - Each state computation involves constant work, making it \( O(targetLength \cdot wordLength) \).
- The function
- Overall Time Complexity:
The total time complexity is:
\( O(w \cdot l + targetLength \cdot wordLength) \).
Space Complexity
- Character Frequency Table:
- The space required for storing the frequency of each character in each column is \( O(26 \cdot wordLength) \).
- Memoization Table:
- The memoization table stores results for \( O(targetLength \cdot wordLength) \) states, requiring \( O(targetLength \cdot wordLength) \) space.
- Auxiliary Space:
- The recursive stack depth is \( O(targetLength) \), corresponding to the target length.
- Overall Space Complexity:
The total space complexity is: \( O(26 \cdot wordLength + targetLength \cdot wordLength + targetLength) \), which simplifies to \( O(targetLength \cdot wordLength) \) as the dominant term.
Approach 01:
-
C++
-
Python
class Solution {
public:
const int MOD = 1e9+7;
int numWays(vector<string>& words, string target) {
int wordLength = words[0].size();
int targetLength = target.length();
// Build frequency table for each character at every column
vector<vector<long long>> charFrequency(26, vector<long long>(wordLength, 0));
for (int column = 0; column < wordLength; column++) {
for (string &word : words) {
charFrequency[word[column] - 'a'][column]++;
}
}
// dp[targetLength][columnLength]: Total ways to form target of length targetLength using columnLength columns
vector<vector<long long>> dp(targetLength + 1, vector<long long>(wordLength + 1, 0));
dp[0][0] = 1; // Base case: 1 way to form empty target
for (int targetIndex = 0; targetIndex <= targetLength; targetIndex++) {
for (int columnIndex = 0; columnIndex <= wordLength; columnIndex++) {
// Skip current column
if (columnIndex < wordLength)
dp[targetIndex][columnIndex + 1] = (dp[targetIndex][columnIndex + 1] + dp[targetIndex][columnIndex]) % MOD;
// Use current column
if (targetIndex < targetLength && columnIndex < wordLength) {
dp[targetIndex + 1][columnIndex + 1] = (dp[targetIndex + 1][columnIndex + 1] +
charFrequency[target[targetIndex] - 'a'][columnIndex] * dp[targetIndex][columnIndex]) % MOD;
}
}
}
return dp[targetLength][wordLength];
}
};
class Solution:
MOD = int(1e9+7)
def numWays(self, words, target):
wordLength = len(words[0])
targetLength = len(target)
# Build frequency table for each character at every column
charFrequency = [[0] * wordLength for _ in range(26)]
for column in range(wordLength):
for word in words:
charFrequency[ord(word[column]) - ord('a')][column] += 1
# dp[targetLength][columnLength]: Total ways to form target of length targetLength using columnLength columns
dp = [[0] * (wordLength + 1) for _ in range(targetLength + 1)]
dp[0][0] = 1 # Base case: 1 way to form empty target
for targetIndex in range(targetLength + 1):
for columnIndex in range(wordLength + 1):
# Skip current column
if columnIndex < wordLength:
dp[targetIndex][columnIndex + 1] = (dp[targetIndex][columnIndex + 1] + dp[targetIndex][columnIndex]) % self.MOD
# Use current column
if targetIndex < targetLength and columnIndex < wordLength:
dp[targetIndex + 1][columnIndex + 1] = (dp[targetIndex + 1][columnIndex + 1] +
charFrequency[ord(target[targetIndex]) - ord('a')][columnIndex] * dp[targetIndex][columnIndex]) % self.MOD
return dp[targetLength][wordLength]
Time Complexity
- Building Character Frequency Table:
- Iterating over each column of
words, for a total of \( O(w \cdot l) \), where \( w \) is the number of words and \( l \) is the length of each word.
- Iterating over each column of
- Dynamic Programming Table Updates:
- The outer loop iterates over \( targetLength + 1 \) states.
- The inner loop iterates over \( wordLength + 1 \) states.
- Each state computation is \( O(1) \), resulting in \( O(targetLength \cdot wordLength) \).
- Overall Time Complexity:
The total time complexity is: \( O(w \cdot l + targetLength \cdot wordLength) \).
Space Complexity
- Character Frequency Table:
- The space required for storing the frequency of each character in each column is \( O(26 \cdot wordLength) \).
- Dynamic Programming Table:
- The table requires \( O((targetLength + 1) \cdot (wordLength + 1)) \) space.
- Overall Space Complexity:
The total space complexity is: \( O(26 \cdot wordLength + targetLength \cdot wordLength) \), which simplifies to \( O(targetLength \cdot wordLength) \) as the dominant term.