Skip to content

Commit b9dc478

Browse files
authored
feat: add solutions to lc problem: No.3213 (#3224)
No.3213.Construct String with Minimum Cost
1 parent 8932678 commit b9dc478

File tree

8 files changed

+713
-15
lines changed

8 files changed

+713
-15
lines changed

solution/2500-2599/2582.Pass the Pillow/README.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ tags:
1919

2020
<!-- description:start -->
2121

22-
<p><code>n</code> 个人站成一排,按从 <code>1</code> 到 <code>n</code> 编号。</p>
23-
24-
<p>最初,排在队首的第一个人拿着一个枕头。每秒钟,拿着枕头的人会将枕头传递给队伍中的下一个人。一旦枕头到达队首或队尾,传递方向就会改变,队伍会继续沿相反方向传递枕头。</p>
22+
<p><code>n</code> 个人站成一排,按从 <code>1</code> 到 <code>n</code> 编号。最初,排在队首的第一个人拿着一个枕头。每秒钟,拿着枕头的人会将枕头传递给队伍中的下一个人。一旦枕头到达队首或队尾,传递方向就会改变,队伍会继续沿相反方向传递枕头。</p>
2523

2624
<ul>
2725
<li>例如,当枕头到达第 <code>n</code> 个人时,TA 会将枕头传递给第 <code>n - 1</code> 个人,然后传递给第 <code>n - 2</code> 个人,依此类推。</li>
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
impl Solution {
22
pub fn max_bottles_drunk(mut num_bottles: i32, mut num_exchange: i32) -> i32 {
33
let mut ans = num_bottles;
4-
4+
55
while num_bottles >= num_exchange {
66
num_bottles -= num_exchange;
77
num_exchange += 1;
88
ans += 1;
99
num_bottles += 1;
1010
}
11-
11+
1212
ans
1313
}
1414
}

solution/3200-3299/3213.Construct String with Minimum Cost/README.md

+241-5
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,268 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3200-3299/3213.Co
7777

7878
<!-- solution:start -->
7979

80-
### 方法一
80+
### 方法一:字符串哈希 + 动态规划 + 枚举长度
81+
82+
我们定义 $f[i]$ 表示构造 $\textit{target}$ 前 $i$ 个字符的最小代价,初始时 $f[0] = 0$,其余值均为无穷大。答案为 $f[n]$,其中 $n$ 是 $\textit{target}$ 的长度。
83+
84+
对于当前 $f[i]$,考虑枚举单词的长度 $j$,如果 $j \leq i$,那么我们可以考虑从 $i - j + 1$ 到 $i$ 这段区间的哈希值,如果这个哈希值对应的单词存在,那么我们可以转移从 $f[i - j]$ 转移到 $f[i]$。状态转移方程如下:
85+
86+
$$
87+
f[i] = \min(f[i], f[i - j] + \textit{cost}[k])
88+
$$
89+
90+
其中 $textit{cost}[k]$ 表示长度为 $j$ 的单词且哈希值与 $\textit{target}[i - j + 1, i]$ 相同的单词的最小代价。
91+
92+
时间复杂度 $O(n \times \sqrt{L})$,空间复杂度 $O(n)$。其中 $n$ 是 $\textit{target}$ 的长度,而 $L$ 是数组 $\textit{words}$ 中所有单词的长度之和。
8193

8294
<!-- tabs:start -->
8395

8496
#### Python3
8597

8698
```python
87-
99+
class Solution:
100+
def minimumCost(self, target: str, words: List[str], costs: List[int]) -> int:
101+
base, mod = 13331, 998244353
102+
n = len(target)
103+
h = [0] * (n + 1)
104+
p = [1] * (n + 1)
105+
for i, c in enumerate(target, 1):
106+
h[i] = (h[i - 1] * base + ord(c)) % mod
107+
p[i] = (p[i - 1] * base) % mod
108+
f = [0] + [inf] * n
109+
ss = sorted(set(map(len, words)))
110+
d = defaultdict(lambda: inf)
111+
min = lambda a, b: a if a < b else b
112+
for w, c in zip(words, costs):
113+
x = 0
114+
for ch in w:
115+
x = (x * base + ord(ch)) % mod
116+
d[x] = min(d[x], c)
117+
for i in range(1, n + 1):
118+
for j in ss:
119+
if j > i:
120+
break
121+
x = (h[i] - h[i - j] * p[j]) % mod
122+
f[i] = min(f[i], f[i - j] + d[x])
123+
return f[n] if f[n] < inf else -1
88124
```
89125

90126
#### Java
91127

92128
```java
93-
129+
class Hashing {
130+
private final long[] p;
131+
private final long[] h;
132+
private final long mod;
133+
134+
public Hashing(String word, long base, int mod) {
135+
int n = word.length();
136+
p = new long[n + 1];
137+
h = new long[n + 1];
138+
p[0] = 1;
139+
this.mod = mod;
140+
for (int i = 1; i <= n; i++) {
141+
p[i] = p[i - 1] * base % mod;
142+
h[i] = (h[i - 1] * base + word.charAt(i - 1)) % mod;
143+
}
144+
}
145+
146+
public long query(int l, int r) {
147+
return (h[r] - h[l - 1] * p[r - l + 1] % mod + mod) % mod;
148+
}
149+
}
150+
151+
class Solution {
152+
public int minimumCost(String target, String[] words, int[] costs) {
153+
final int base = 13331;
154+
final int mod = 998244353;
155+
final int inf = Integer.MAX_VALUE / 2;
156+
157+
int n = target.length();
158+
Hashing hashing = new Hashing(target, base, mod);
159+
160+
int[] f = new int[n + 1];
161+
Arrays.fill(f, inf);
162+
f[0] = 0;
163+
164+
TreeSet<Integer> ss = new TreeSet<>();
165+
for (String w : words) {
166+
ss.add(w.length());
167+
}
168+
169+
Map<Long, Integer> d = new HashMap<>();
170+
for (int i = 0; i < words.length; i++) {
171+
long x = 0;
172+
for (char c : words[i].toCharArray()) {
173+
x = (x * base + c) % mod;
174+
}
175+
d.merge(x, costs[i], Integer::min);
176+
}
177+
178+
for (int i = 1; i <= n; i++) {
179+
for (int j : ss) {
180+
if (j > i) {
181+
break;
182+
}
183+
long x = hashing.query(i - j + 1, i);
184+
f[i] = Math.min(f[i], f[i - j] + d.getOrDefault(x, inf));
185+
}
186+
}
187+
188+
return f[n] >= inf ? -1 : f[n];
189+
}
190+
}
94191
```
95192

96193
#### C++
97194

98195
```cpp
99-
196+
class Hashing {
197+
private:
198+
vector<long> p, h;
199+
long mod;
200+
201+
public:
202+
Hashing(const string& word, long base, long mod)
203+
: p(word.size() + 1, 1)
204+
, h(word.size() + 1, 0)
205+
, mod(mod) {
206+
for (int i = 1; i <= word.size(); ++i) {
207+
p[i] = p[i - 1] * base % mod;
208+
h[i] = (h[i - 1] * base + word[i - 1]) % mod;
209+
}
210+
}
211+
212+
long query(int l, int r) {
213+
return (h[r] - h[l - 1] * p[r - l + 1] % mod + mod) % mod;
214+
}
215+
};
216+
217+
class Solution {
218+
public:
219+
int minimumCost(string target, vector<string>& words, vector<int>& costs) {
220+
const int base = 13331;
221+
const int mod = 998244353;
222+
const int inf = INT_MAX / 2;
223+
224+
int n = target.size();
225+
Hashing hashing(target, base, mod);
226+
227+
vector<int> f(n + 1, inf);
228+
f[0] = 0;
229+
230+
set<int> ss;
231+
for (const string& w : words) {
232+
ss.insert(w.size());
233+
}
234+
235+
unordered_map<long, int> d;
236+
for (int i = 0; i < words.size(); ++i) {
237+
long x = 0;
238+
for (char c : words[i]) {
239+
x = (x * base + c) % mod;
240+
}
241+
d[x] = d.find(x) == d.end() ? costs[i] : min(d[x], costs[i]);
242+
}
243+
244+
for (int i = 1; i <= n; ++i) {
245+
for (int j : ss) {
246+
if (j > i) {
247+
break;
248+
}
249+
long x = hashing.query(i - j + 1, i);
250+
if (d.contains(x)) {
251+
f[i] = min(f[i], f[i - j] + d[x]);
252+
}
253+
}
254+
}
255+
256+
return f[n] >= inf ? -1 : f[n];
257+
}
258+
};
100259
```
101260

102261
#### Go
103262

104263
```go
105-
264+
type Hashing struct {
265+
p []int64
266+
h []int64
267+
mod int64
268+
}
269+
270+
func NewHashing(word string, base, mod int64) *Hashing {
271+
n := len(word)
272+
p := make([]int64, n+1)
273+
h := make([]int64, n+1)
274+
p[0] = 1
275+
for i := 1; i <= n; i++ {
276+
p[i] = p[i-1] * base % mod
277+
h[i] = (h[i-1]*base + int64(word[i-1])) % mod
278+
}
279+
return &Hashing{p, h, mod}
280+
}
281+
282+
func (hs *Hashing) query(l, r int) int64 {
283+
return (hs.h[r] - hs.h[l-1]*hs.p[r-l+1]%hs.mod + hs.mod) % hs.mod
284+
}
285+
286+
func minimumCost(target string, words []string, costs []int) int {
287+
const base = 13331
288+
const mod = 998244353
289+
const inf = math.MaxInt32 / 2
290+
291+
n := len(target)
292+
hashing := NewHashing(target, base, mod)
293+
294+
f := make([]int, n+1)
295+
for i := range f {
296+
f[i] = inf
297+
}
298+
f[0] = 0
299+
300+
ss := make(map[int]struct{})
301+
for _, w := range words {
302+
ss[len(w)] = struct{}{}
303+
}
304+
lengths := make([]int, 0, len(ss))
305+
for length := range ss {
306+
lengths = append(lengths, length)
307+
}
308+
sort.Ints(lengths)
309+
310+
d := make(map[int64]int)
311+
for i, w := range words {
312+
var x int64
313+
for _, c := range w {
314+
x = (x*base + int64(c)) % mod
315+
}
316+
if existingCost, exists := d[x]; exists {
317+
if costs[i] < existingCost {
318+
d[x] = costs[i]
319+
}
320+
} else {
321+
d[x] = costs[i]
322+
}
323+
}
324+
325+
for i := 1; i <= n; i++ {
326+
for _, j := range lengths {
327+
if j > i {
328+
break
329+
}
330+
x := hashing.query(i-j+1, i)
331+
if cost, ok := d[x]; ok {
332+
f[i] = min(f[i], f[i-j]+cost)
333+
}
334+
}
335+
}
336+
337+
if f[n] >= inf {
338+
return -1
339+
}
340+
return f[n]
341+
}
106342
```
107343

108344
<!-- tabs:end -->

0 commit comments

Comments
 (0)