MOD = 10 ** 9 + 7
class Solution:
def numWays(self, words, target):
from collections import Counter
m, n = len(words[0]), len(target)
# Precompute frequency of each character at each position
freq = [Counter() for _ in range(m)]
for word in words:
for i, c in enumerate(word):
freq[i][c] += 1
dp = [0] * (n + 1)
dp[0] = 1
for i in range(m):
for j in range(n-1, -1, -1):
c = target[j]
dp[j+1] = (dp[j+1] + dp[j] * freq[i][c]) % MOD
return dp[n]
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
class Solution {
public:
int numWays(vector<string>& words, string target) {
const int MOD = 1e9 + 7;
int m = words[0].size(), n = target.size();
vector<vector<int>> freq(m, vector<int>(26, 0));
for (auto& word : words) {
for (int i = 0; i < m; ++i) {
freq[i][word[i] - 'a']++;
}
}
vector<long long> dp(n + 1, 0);
dp[0] = 1;
for (int i = 0; i < m; ++i) {
for (int j = n - 1; j >= 0; --j) {
int c = target[j] - 'a';
dp[j + 1] = (dp[j + 1] + dp[j] * freq[i][c]) % MOD;
}
}
return dp[n];
}
};
import java.util.*;
class Solution {
public int numWays(String[] words, String target) {
int MOD = 1000000007;
int m = words[0].length(), n = target.length();
int[][] freq = new int[m][26];
for (String word : words) {
for (int i = 0; i < m; ++i) {
freq[i][word.charAt(i) - 'a']++;
}
}
long[] dp = new long[n + 1];
dp[0] = 1;
for (int i = 0; i < m; ++i) {
for (int j = n - 1; j >= 0; --j) {
char c = target.charAt(j);
dp[j + 1] = (dp[j + 1] + dp[j] * freq[i][c - 'a']) % MOD;
}
}
return (int)dp[n];
}
}
var numWays = function(words, target) {
const MOD = 1e9 + 7;
const m = words[0].length, n = target.length;
// Precompute frequencies
const freq = Array.from({length: m}, () => Array(26).fill(0));
for (const word of words) {
for (let i = 0; i < m; ++i) {
freq[i][word.charCodeAt(i) - 97]++;
}
}
let dp = new Array(n + 1).fill(0);
dp[0] = 1;
for (let i = 0; i < m; ++i) {
for (let j = n - 1; j >= 0; --j) {
const c = target.charCodeAt(j) - 97;
dp[j+1] = (dp[j+1] + dp[j] * freq[i][c]) % MOD;
}
}
return dp[n];
};
Given a list of strings words
where each string has the same length, and a target string target
, you need to determine how many ways you can form target
by picking one character from each column (i.e., all characters at the same index in every word) in words
. For each character in target
, you must pick it from a column to the right of (or at) the position of the previous character you picked (no going back to earlier columns). Every column can only be used once and you cannot reuse characters from the same column for different target positions.
target
modulo 109 + 7.words
have the same length; target
is not longer than any word.
At first glance, you might consider a brute-force approach: for every character in target
, try every possible way to pick that character from the available columns in words
, recursively or via backtracking. However, this quickly becomes infeasible because the number of combinations grows exponentially with the length of words[0]
and target
.
To optimize, we notice two key ideas:
target
must be matched with a column in words
at or after the previous character's column.target
to the columns of words
, where at each step, we choose a column and count the available characters.
We solve the problem using dynamic programming and precomputation:
words
, count how many times each letter ('a'-'z') appears across all words at that column position.dp[j]
be the number of ways to form the first j
characters of target
using the first i
columns of words
.dp[0] = 1
(1 way to form an empty target).i
in words
:
j
in target
, from last to first (so previous dp values are not overwritten):target[j]
appears in this column, add dp[j] * (frequency of target[j] in column i)
to dp[j+1]
.dp[target.length]
after processing all columns.This approach is efficient because it avoids redundant calculations and leverages the fact that the order of columns matters, but the order of words within a column does not.
Let's walk through an example:
Input:
words = ["acca","bbbb","caca"]
target = "aba"
dp = [1, 0, 0, 0]
(since target has length 3).dp
:
dp[1] += dp[0] * 1 = 1
dp[2] += dp[1] * 1 = 1
dp[3]
remains 0dp[3] += dp[2] * 2 = 2
dp = [1, 1, 1, 2]
, so answer is 2
.There are 2 ways to form "aba" from the columns of the given words.
Brute-force:
target
, you could try every possible column and every possible word, leading to exponential time: O(mn * k), where m = word length, n = target length, k = number of words.This is efficient and feasible for the given constraints.
The solution leverages dynamic programming and precomputed character frequencies to efficiently count the number of ways to form a target string from columns of multiple words. By iterating through columns and updating possible ways to form each prefix of the target, we avoid redundant work and reduce the problem from exponential to polynomial time. The approach is elegant because it turns a seemingly recursive, combinatorial challenge into a simple DP update, making it both fast and easy to implement.