@@ -77,32 +77,268 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3200-3299/3213.Co
77
77
78
78
<!-- solution:start -->
79
79
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}$ 中所有单词的长度之和。
81
93
82
94
<!-- tabs:start -->
83
95
84
96
#### Python3
85
97
86
98
``` 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
88
124
```
89
125
90
126
#### Java
91
127
92
128
``` 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
+ }
94
191
```
95
192
96
193
#### C++
97
194
98
195
``` 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
+ };
100
259
```
101
260
102
261
#### Go
103
262
104
263
``` 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
+ }
106
342
```
107
343
108
344
<!-- tabs:end -->
0 commit comments