Construct String with Minimum Cost (Easy)
MediumUpdated: Jul 7, 2025
Practice on:
Problem
You are given a string target, an array of strings words, and an integer array costs, both arrays of the same length.
Imagine an empty string s.
You can perform the following operation any number of times (including zero):
- Choose an index
iin the range[0, words.length - 1]. - Append
words[i]tos. - The cost of operation is
costs[i].
Return the minimum cost to make s equal to target. If it's not possible, return -1.
Examples
Example 1:
Input: target = "abcdef", words = ["abdef","abc","d","def","ef"], costs =
[100,1,1,10,5]
Output: 7
Explanation:
The minimum cost can be achieved by performing the following operations:
* Select index 1 and append `"abc"` to `s` at a cost of 1, resulting in `s = "abc"`.
* Select index 2 and append `"d"` to `s` at a cost of 1, resulting in `s = "abcd"`.
* Select index 4 and append `"ef"` to `s` at a cost of 5, resulting in `s = "abcdef"`.
Example 2:
Input: target = "aaaa", words = ["z","zz","zzz"], costs = [1,10,100]
Output: -1
Explanation:
It is impossible to make `s` equal to `target`, so we return -1.
Constraints:
1 <= target.length <= 20001 <= words.length == costs.length <= 501 <= words[i].length <= target.lengthtargetandwords[i]consist only of lowercase English letters.1 <= costs[i] <= 10^5
Solution
Method 1 – Dynamic Programming (Substring Matching)
Intuition
We want to build the target string by appending words from the list, minimizing the total cost. Since the constraints are small, we can use a simple DP where for each position, we check all words that could end at that position and update the minimum cost.
Approach
- Let
dp[i]be the minimum cost to buildtarget[:i]. Initializedp[0]= 0, others to infinity. - For each position i in target (1 to n):
- For each word, if
target[i-len(word):i] == word, updatedp[i] = min(dp[i], dp[i-len(word)] + cost).
- For each word, if
- The answer is
dp[n]if it is not infinity, else -1.
Code
C++
class Solution {
public:
int minimumCost(string target, vector<string>& words, vector<int>& costs) {
int n = target.size();
vector<int> dp(n+1, 1e9);
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < words.size(); ++j) {
int m = words[j].size();
if (i >= m && target.substr(i-m, m) == words[j]) {
dp[i] = min(dp[i], dp[i-m] + costs[j]);
}
}
}
return dp[n] >= 1e9 ? -1 : dp[n];
}
};
Go
func MinimumCost(target string, words []string, costs []int) int {
n := len(target)
dp := make([]int, n+1)
for i := range dp { dp[i] = 1e9 }
dp[0] = 0
for i := 1; i <= n; i++ {
for j, w := range words {
m := len(w)
if i >= m && target[i-m:i] == w {
if dp[i-m]+costs[j] < dp[i] {
dp[i] = dp[i-m] + costs[j]
}
}
}
}
if dp[n] >= 1e9 { return -1 }
return dp[n]
}
Java
class Solution {
public int minimumCost(String target, String[] words, int[] costs) {
int n = target.length();
int[] dp = new int[n+1];
Arrays.fill(dp, (int)1e9);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < words.length; j++) {
int m = words[j].length();
if (i >= m && target.substring(i-m, i).equals(words[j])) {
dp[i] = Math.min(dp[i], dp[i-m] + costs[j]);
}
}
}
return dp[n] >= 1e9 ? -1 : dp[n];
}
}
Kotlin
class Solution {
fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int {
val n = target.length
val dp = IntArray(n+1) { 1_000_000_000 }
dp[0] = 0
for (i in 1..n) {
for (j in words.indices) {
val m = words[j].length
if (i >= m && target.substring(i-m, i) == words[j]) {
dp[i] = minOf(dp[i], dp[i-m] + costs[j])
}
}
}
return if (dp[n] >= 1_000_000_000) -1 else dp[n]
}
}
Python
class Solution:
def minimumCost(self, target: str, words: list[str], costs: list[int]) -> int:
n = len(target)
dp = [float('inf')] * (n+1)
dp[0] = 0
for i in range(1, n+1):
for w, c in zip(words, costs):
m = len(w)
if i >= m and target[i-m:i] == w:
dp[i] = min(dp[i], dp[i-m] + c)
return -1 if dp[n] == float('inf') else dp[n]
Rust
impl Solution {
pub fn minimum_cost(target: String, words: Vec<String>, costs: Vec<i32>) -> i32 {
let n = target.len();
let t = target.as_bytes();
let mut dp = vec![i32::MAX; n+1];
dp[0] = 0;
for i in 1..=n {
for (w, &c) in words.iter().zip(costs.iter()) {
let m = w.len();
if i >= m && &t[i-m..i] == w.as_bytes() {
dp[i] = dp[i].min(dp[i-m] + c);
}
}
}
if dp[n] == i32::MAX { -1 } else { dp[n] }
}
}
TypeScript
class Solution {
minimumCost(target: string, words: string[], costs: number[]): number {
const n = target.length;
const dp = Array(n+1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= n; i++) {
for (let j = 0; j < words.length; j++) {
const m = words[j].length;
if (i >= m && target.slice(i-m, i) === words[j]) {
dp[i] = Math.min(dp[i], dp[i-m] + costs[j]);
}
}
}
return dp[n] === Infinity ? -1 : dp[n];
}
}
Complexity
- ⏰ Time complexity:
O(n * W * L), where n is the length of target, W is the number of words, and L is the average word length. For each position, we check all words. - 🧺 Space complexity:
O(n), for the DP array.