Number of Ways to Form a Target String Given a Dictionary
Leetcode Daily Challenge (16th April, 2023)
Problem Statement:-
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.To form the
i<sup>th</sup>
character (0-indexed) oftarget
, you can choose thek<sup>th</sup>
character of thej<sup>th</sup>
string inwords
iftarget[i] = words[j][k]
.Once you use the
k<sup>th</sup>
character of thej<sup>th</sup>
string ofwords
, you can no longer use thex<sup>th</sup>
character of any string inwords
wherex <= k
. In other words, all characters to the left of or at indexk
become 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 10<sup>9</sup> + 7
.
Link: https://leetcode.com/problems/number-of-ways-to-form-a-target-string-given-a-dictionary/description/
Problem Explanation with examples:-
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 <= 1000
1 <= words[i].length <= 1000
All strings in
words
have the same length.1 <= target.length <= 1000
words[i]
andtarget
contain only lowercase English letters.
Intuition:-
DP will be used to solve this problem.
The changing parameters here are the index of the current character in the target string and the index of the current character in the words array.
First of all, we will create a hashmap mp which will store the number of times each character appears at each index in the words array to make the computation faster.
We will create a recursive function solve(i,k) which returns the number of ways we can form the target string if we are at the ith character of the target string and the kth character of the words array.
When we reach the end of the target string, we return 1 as we have formed the target string and when we reach the end of the words array, we return 0 as we cannot form the target string.
Solution:-
Create a variable mod and initialize it to 10^9 + 7.
Create a hashmap mp and initialize it to an empty hashmap.
Create a for loop which iterates over the words array and in each iteration, we create a for loop which iterates over the current word and in each iteration, we increment mp[(i,c)] by 1.
Create a recursive function solve(i,k) which returns the number of ways we can form the target string if we are at the ith character of the target string and the kth character of the words array.
If i == len(target), return 1 as we have reached the end of the target string.
If k == len(words[0]), return 0 as we have reached the end of the words array.
Create a variable c and initialize it to the ith character of the target string.
Create a variable res and initialize it to the result of the recursive call solve(i,k+1) which means we are not taking the kth character of the words array.
Update res to res + (mp[(k,c)] * solve(i+1,k+1)). Here, we are taking the kth character of the words array and adding it to the target string.
Return res % mod.
Return the result of the recursive call solve(0,0).
Code:-
JAVA Solution
class Solution {
public int numWays(String[] words, String target) {
int mod = 1000000007;
Map<Pair<Integer,Character>, Integer> mp = new HashMap<>();
for (String w : words) {
for (int i = 0; i < w.length(); i++) {
Pair<Integer,Character> key = new Pair<>(i, w.charAt(i));
mp.put(key, mp.getOrDefault(key, 0) + 1);
}
}
Map<Pair<Integer,Integer>, Integer> cache = new HashMap<>();
Function<Pair<Integer,Integer>, Integer> solve = new Function<Pair<Integer,Integer>, Integer>() {
public Integer apply(Pair<Integer,Integer> pair) {
int i = pair.getKey();
int k = pair.getValue();
if (i == target.length()) {
return 1;
}
if (k == words[0].length()) {
return 0;
}
char c = target.charAt(i);
Pair<Integer,Character> key = new Pair<>(k, c);
if (cache.containsKey(pair)) {
return cache.get(pair);
}
int res = solve.apply(new Pair<>(i,k+1));
res = (res + (mp.getOrDefault(key, 0) * solve.apply(new Pair<>(i+1,k+1)))) % mod;
cache.put(pair, res);
return res;
}
};
return solve.apply(new Pair<>(0,0));
}
}
Python Solution
class Solution:
def numWays(self, words: List[str], target: str) -> int:
mod = 10**9 + 7
mp = defaultdict(int)
for w in words:
for i,c in enumerate(w):
mp[(i,c)] += 1
@cache
def solve(i,k):
if i == len(target):
return 1
if k == len(words[0]):
return 0
c = target[i]
res = solve(i,k+1)
res += (mp[(k,c)] * solve(i+1,k+1))
return (res % mod)
return solve(0,0)
Complexity Analysis:-
TIME:-
The time complexity of the given Python code is O(NML), where N is the length of the input list words
, M is the length of each word in words
, and L is the length of the target
string. This is because the code uses memoization to cache previously computed values and avoids recomputing them, resulting in significant time savings.
SPACE:-
The space complexity of the code is also O(NML), as the memoization cache requires this much space to store the computed results. Since the recursion depth is at most L, the maximum depth of the call stack is also O(L). Overall, the space complexity of the algorithm is reasonable given the size of the input data.
References:-
@cache in Python