Number of Ways to Form a Target String Given a Dictionary

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) of target, you can choose the k<sup>th</sup> character of the j<sup>th</sup> string in words if target[i] = words[j][k].

  • Once you use the k<sup>th</sup> character of the j<sup>th</sup> string of words, you can no longer use the x<sup>th</sup> 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.

  • 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] and target 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:-

Connect with me:-

Did you find this article valuable?

Support Leeting-LCS by becoming a sponsor. Any amount is appreciated!