Skip to content

Commit a7c9dd4

Browse files
authored
feat: add solutions to lc problem: No.3253 (#3415)
No.3253.Construct String with Minimum Cost (Easy)
1 parent d8fd6a9 commit a7c9dd4

File tree

9 files changed

+1015
-0
lines changed

9 files changed

+1015
-0
lines changed
Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
---
2+
comments: true
3+
difficulty: 中等
4+
edit_url: https://github.com/doocs/leetcode/edit/main/solution/3200-3299/3253.Construct%20String%20with%20Minimum%20Cost%20%28Easy%29/README.md
5+
---
6+
7+
<!-- problem:start -->
8+
9+
# [3253. Construct String with Minimum Cost (Easy) 🔒](https://leetcode.cn/problems/construct-string-with-minimum-cost-easy)
10+
11+
[English Version](/solution/3200-3299/3253.Construct%20String%20with%20Minimum%20Cost%20%28Easy%29/README_EN.md)
12+
13+
## 题目描述
14+
15+
<!-- description:start -->
16+
17+
<p>You are given a string <code>target</code>, an array of strings <code>words</code>, and an integer array <code>costs</code>, both arrays of the same length.</p>
18+
19+
<p>Imagine an empty string <code>s</code>.</p>
20+
21+
<p>You can perform the following operation any number of times (including <strong>zero</strong>):</p>
22+
23+
<ul>
24+
<li>Choose an index <code>i</code> in the range <code>[0, words.length - 1]</code>.</li>
25+
<li>Append <code>words[i]</code> to <code>s</code>.</li>
26+
<li>The cost of operation is <code>costs[i]</code>.</li>
27+
</ul>
28+
29+
<p>Return the <strong>minimum</strong> cost to make <code>s</code> equal to <code>target</code>. If it&#39;s not possible, return -1.</p>
30+
31+
<p>&nbsp;</p>
32+
<p><strong class="example">Example 1:</strong></p>
33+
34+
<div class="example-block">
35+
<p><strong>Input:</strong> <span class="example-io">target = &quot;abcdef&quot;, words = [&quot;abdef&quot;,&quot;abc&quot;,&quot;d&quot;,&quot;def&quot;,&quot;ef&quot;], costs = [100,1,1,10,5]</span></p>
36+
37+
<p><strong>Output:</strong> <span class="example-io">7</span></p>
38+
39+
<p><strong>Explanation:</strong></p>
40+
41+
<p>The minimum cost can be achieved by performing the following operations:</p>
42+
43+
<ul>
44+
<li>Select index 1 and append <code>&quot;abc&quot;</code> to <code>s</code> at a cost of 1, resulting in <code>s = &quot;abc&quot;</code>.</li>
45+
<li>Select index 2 and append <code>&quot;d&quot;</code> to <code>s</code> at a cost of 1, resulting in <code>s = &quot;abcd&quot;</code>.</li>
46+
<li>Select index 4 and append <code>&quot;ef&quot;</code> to <code>s</code> at a cost of 5, resulting in <code>s = &quot;abcdef&quot;</code>.</li>
47+
</ul>
48+
</div>
49+
50+
<p><strong class="example">Example 2:</strong></p>
51+
52+
<div class="example-block">
53+
<p><strong>Input:</strong> <span class="example-io">target = &quot;aaaa&quot;, words = [&quot;z&quot;,&quot;zz&quot;,&quot;zzz&quot;], costs = [1,10,100]</span></p>
54+
55+
<p><strong>Output:</strong> <span class="example-io">-1</span></p>
56+
57+
<p><strong>Explanation:</strong></p>
58+
59+
<p>It is impossible to make <code>s</code> equal to <code>target</code>, so we return -1.</p>
60+
</div>
61+
62+
<p>&nbsp;</p>
63+
<p><strong>Constraints:</strong></p>
64+
65+
<ul>
66+
<li><code>1 &lt;= target.length &lt;= 2000</code></li>
67+
<li><code>1 &lt;= words.length == costs.length &lt;= 50</code></li>
68+
<li><code>1 &lt;= words[i].length &lt;= target.length</code></li>
69+
<li><code>target</code> and <code>words[i]</code> consist only of lowercase English letters.</li>
70+
<li><code>1 &lt;= costs[i] &lt;= 10<sup>5</sup></code></li>
71+
</ul>
72+
73+
<!-- description:end -->
74+
75+
## 解法
76+
77+
<!-- solution:start -->
78+
79+
### 方法一:字典树 + 记忆化搜索
80+
81+
我们首先创建一个字典树 $\textit{trie}$,字典树的每个节点包含一个长度为 $26$ 的数组 $\textit{children}$,数组中的每个元素都是一个指向下一个节点的指针。字典树的每个节点还包含一个 $\textit{cost}$ 变量,表示从根节点到当前节点的最小花费。
82+
83+
我们遍历 $\textit{words}$ 数组,将每个单词插入到字典树中,同时更新每个节点的 $\textit{cost}$ 变量。
84+
85+
接下来,我们定义一个记忆化搜索函数 $\textit{dfs}(i)$,表示从 $\textit{target}[i]$ 开始构造字符串的最小花费。那么答案就是 $\textit{dfs}(0)$。
86+
87+
函数 $\textit{dfs}(i)$ 的计算过程如下:
88+
89+
- 如果 $i \geq \textit{len}(\textit{target})$,表示已经构造完整个字符串,返回 $0$。
90+
- 否则,我们从 $\textit{trie}$ 的根节点开始,遍历 $\textit{target}[i]$ 开始的所有后缀,找到最小花费,即 $\textit{trie}$ 中的 $\textit{cost}$ 变量,加上 $\textit{dfs}(j+1)$ 的结果,其中 $j$ 是 $\textit{target}[i]$ 开始的后缀的结束位置。
91+
92+
最后,如果 $\textit{dfs}(0) < \textit{inf}$,返回 $\textit{dfs}(0)$,否则返回 $-1$。
93+
94+
时间复杂度 $O(n^2 + L)$,空间复杂度 $O(n + L)$。其中 $n$ 是 $\textit{target}$ 的长度,而 $L$ 是 $\textit{words}$ 数组中所有单词的长度之和。
95+
96+
<!-- tabs:start -->
97+
98+
#### Python3
99+
100+
```python
101+
class Trie:
102+
def __init__(self):
103+
self.children: List[Optional[Trie]] = [None] * 26
104+
self.cost = inf
105+
106+
def insert(self, word: str, cost: int):
107+
node = self
108+
for c in word:
109+
idx = ord(c) - ord("a")
110+
if node.children[idx] is None:
111+
node.children[idx] = Trie()
112+
node = node.children[idx]
113+
node.cost = min(node.cost, cost)
114+
115+
116+
class Solution:
117+
def minimumCost(self, target: str, words: List[str], costs: List[int]) -> int:
118+
@cache
119+
def dfs(i: int) -> int:
120+
if i >= len(target):
121+
return 0
122+
ans = inf
123+
node = trie
124+
for j in range(i, len(target)):
125+
idx = ord(target[j]) - ord("a")
126+
if node.children[idx] is None:
127+
return ans
128+
node = node.children[idx]
129+
ans = min(ans, node.cost + dfs(j + 1))
130+
return ans
131+
132+
trie = Trie()
133+
for word, cost in zip(words, costs):
134+
trie.insert(word, cost)
135+
ans = dfs(0)
136+
return ans if ans < inf else -1
137+
```
138+
139+
#### Java
140+
141+
```java
142+
class Trie {
143+
public final int inf = 1 << 29;
144+
public Trie[] children = new Trie[26];
145+
public int cost = inf;
146+
147+
public void insert(String word, int cost) {
148+
Trie node = this;
149+
for (char c : word.toCharArray()) {
150+
int idx = c - 'a';
151+
if (node.children[idx] == null) {
152+
node.children[idx] = new Trie();
153+
}
154+
node = node.children[idx];
155+
}
156+
node.cost = Math.min(node.cost, cost);
157+
}
158+
}
159+
160+
class Solution {
161+
private Trie trie = new Trie();
162+
private char[] target;
163+
private Integer[] f;
164+
165+
public int minimumCost(String target, String[] words, int[] costs) {
166+
for (int i = 0; i < words.length; ++i) {
167+
trie.insert(words[i], costs[i]);
168+
}
169+
this.target = target.toCharArray();
170+
f = new Integer[target.length()];
171+
int ans = dfs(0);
172+
return ans < trie.inf ? ans : -1;
173+
}
174+
175+
private int dfs(int i) {
176+
if (i >= target.length) {
177+
return 0;
178+
}
179+
if (f[i] != null) {
180+
return f[i];
181+
}
182+
f[i] = trie.inf;
183+
Trie node = trie;
184+
for (int j = i; j < target.length; ++j) {
185+
int idx = target[j] - 'a';
186+
if (node.children[idx] == null) {
187+
return f[i];
188+
}
189+
node = node.children[idx];
190+
f[i] = Math.min(f[i], node.cost + dfs(j + 1));
191+
}
192+
return f[i];
193+
}
194+
}
195+
```
196+
197+
#### C++
198+
199+
```cpp
200+
const int inf = 1 << 29;
201+
202+
class Trie {
203+
public:
204+
Trie* children[26]{};
205+
int cost = inf;
206+
207+
void insert(string& word, int cost) {
208+
Trie* node = this;
209+
for (char c : word) {
210+
int idx = c - 'a';
211+
if (!node->children[idx]) {
212+
node->children[idx] = new Trie();
213+
}
214+
node = node->children[idx];
215+
}
216+
node->cost = min(node->cost, cost);
217+
}
218+
};
219+
220+
class Solution {
221+
public:
222+
int minimumCost(string target, vector<string>& words, vector<int>& costs) {
223+
Trie* trie = new Trie();
224+
for (int i = 0; i < words.size(); ++i) {
225+
trie->insert(words[i], costs[i]);
226+
}
227+
int n = target.length();
228+
int f[n];
229+
memset(f, 0, sizeof(f));
230+
auto dfs = [&](auto&& dfs, int i) -> int {
231+
if (i >= n) {
232+
return 0;
233+
}
234+
if (f[i]) {
235+
return f[i];
236+
}
237+
f[i] = inf;
238+
Trie* node = trie;
239+
for (int j = i; j < n; ++j) {
240+
int idx = target[j] - 'a';
241+
if (!node->children[idx]) {
242+
return f[i];
243+
}
244+
node = node->children[idx];
245+
f[i] = min(f[i], node->cost + dfs(dfs, j + 1));
246+
}
247+
return f[i];
248+
};
249+
int ans = dfs(dfs, 0);
250+
return ans < inf ? ans : -1;
251+
}
252+
};
253+
```
254+
255+
#### Go
256+
257+
```go
258+
const inf = 1 << 29
259+
260+
type Trie struct {
261+
children [26]*Trie
262+
cost int
263+
}
264+
265+
func NewTrie() *Trie {
266+
return &Trie{cost: inf}
267+
}
268+
269+
func (t *Trie) insert(word string, cost int) {
270+
node := t
271+
for _, c := range word {
272+
idx := c - 'a'
273+
if node.children[idx] == nil {
274+
node.children[idx] = NewTrie()
275+
}
276+
node = node.children[idx]
277+
}
278+
node.cost = min(node.cost, cost)
279+
}
280+
281+
func minimumCost(target string, words []string, costs []int) int {
282+
trie := NewTrie()
283+
for i, word := range words {
284+
trie.insert(word, costs[i])
285+
}
286+
287+
n := len(target)
288+
f := make([]int, n)
289+
var dfs func(int) int
290+
dfs = func(i int) int {
291+
if i >= n {
292+
return 0
293+
}
294+
if f[i] != 0 {
295+
return f[i]
296+
}
297+
f[i] = inf
298+
node := trie
299+
for j := i; j < n; j++ {
300+
idx := target[j] - 'a'
301+
if node.children[idx] == nil {
302+
return f[i]
303+
}
304+
node = node.children[idx]
305+
f[i] = min(f[i], node.cost+dfs(j+1))
306+
}
307+
return f[i]
308+
}
309+
if ans := dfs(0); ans < inf {
310+
return ans
311+
}
312+
return -1
313+
}
314+
```
315+
316+
#### TypeScript
317+
318+
```ts
319+
const inf = 1 << 29;
320+
321+
class Trie {
322+
children: (Trie | null)[];
323+
cost: number;
324+
325+
constructor() {
326+
this.children = Array(26).fill(null);
327+
this.cost = inf;
328+
}
329+
330+
insert(word: string, cost: number): void {
331+
let node: Trie = this;
332+
for (const c of word) {
333+
const idx = c.charCodeAt(0) - 97;
334+
if (!node.children[idx]) {
335+
node.children[idx] = new Trie();
336+
}
337+
node = node.children[idx]!;
338+
}
339+
node.cost = Math.min(node.cost, cost);
340+
}
341+
}
342+
343+
function minimumCost(target: string, words: string[], costs: number[]): number {
344+
const trie = new Trie();
345+
for (let i = 0; i < words.length; ++i) {
346+
trie.insert(words[i], costs[i]);
347+
}
348+
349+
const n = target.length;
350+
const f: number[] = Array(n).fill(0);
351+
const dfs = (i: number): number => {
352+
if (i >= n) {
353+
return 0;
354+
}
355+
if (f[i]) {
356+
return f[i];
357+
}
358+
f[i] = inf;
359+
let node: Trie | null = trie;
360+
for (let j = i; j < n; ++j) {
361+
const idx = target.charCodeAt(j) - 97;
362+
if (!node?.children[idx]) {
363+
return f[i];
364+
}
365+
node = node.children[idx];
366+
f[i] = Math.min(f[i], node!.cost + dfs(j + 1));
367+
}
368+
return f[i];
369+
};
370+
371+
const ans = dfs(0);
372+
return ans < inf ? ans : -1;
373+
}
374+
```
375+
376+
<!-- tabs:end -->
377+
378+
<!-- solution:end -->
379+
380+
<!-- problem:end -->

0 commit comments

Comments
 (0)