Skip to content

Commit 890e7e5

Browse files
authored
feat: add solutions to lc problem: No.3144 (#3436)
1 parent b875726 commit 890e7e5

File tree

13 files changed

+884
-18
lines changed

13 files changed

+884
-18
lines changed

solution/3100-3199/3144.Minimum Substring Partition of Equal Character Frequency/README.md

Lines changed: 320 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ tags:
8282

8383
为了避免重复计算,我们使用记忆化搜索。
8484

85-
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为字符串 $s$ 的长度。
85+
时间复杂度 $O(n^2)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $n$ 为字符串 $s$ 的长度,而 $|\Sigma|$ 表示字符集的大小,本题中 $|\Sigma| = 26$
8686

8787
<!-- tabs:start -->
8888

@@ -165,7 +165,7 @@ public:
165165
int n = s.size();
166166
int f[n];
167167
memset(f, -1, sizeof(f));
168-
function<int(int)> dfs = [&](int i) {
168+
auto dfs = [&](auto&& dfs, int i) -> int {
169169
if (i >= n) {
170170
return 0;
171171
}
@@ -186,12 +186,12 @@ public:
186186
++cnt[k];
187187
++freq[cnt[k]];
188188
if (freq.size() == 1) {
189-
f[i] = min(f[i], 1 + dfs(j + 1));
189+
f[i] = min(f[i], 1 + dfs(dfs, j + 1));
190190
}
191191
}
192192
return f[i];
193193
};
194-
return dfs(0);
194+
return dfs(dfs, 0);
195195
}
196196
};
197197
```
@@ -276,4 +276,320 @@ function minimumSubstringsInPartition(s: string): number {
276276

277277
<!-- solution:end -->
278278

279+
<!-- solution:start -->
280+
281+
### 方法二:记忆化搜索(优化)
282+
283+
我们可以对方法一进行优化,不需要维护 $\textit{freq}$ 哈希表,只需要维护一个哈希表 $\textit{cnt}$,表示当前子字符串中每个字符出现的次数。另外,维护两个变量 $k$ 和 $m$ 分别表示当前子字符串中的字符种类数和出现次数最多的字符的出现次数。对于一个子串 $s[i..j]$,如果 $j-i+1 = m \times k$,那么这个子串就是一个平衡子串。
284+
285+
时间复杂度 $O(n^2)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $n$ 为字符串 $s$ 的长度,而 $|\Sigma|$ 表示字符集的大小,本题中 $|\Sigma| = 26$。
286+
287+
<!-- tabs:start -->
288+
289+
#### Python3
290+
291+
```python
292+
class Solution:
293+
def minimumSubstringsInPartition(self, s: str) -> int:
294+
@cache
295+
def dfs(i: int) -> int:
296+
if i >= n:
297+
return 0
298+
cnt = defaultdict(int)
299+
m = 0
300+
ans = n - i
301+
for j in range(i, n):
302+
cnt[s[j]] += 1
303+
m = max(m, cnt[s[j]])
304+
if j - i + 1 == m * len(cnt):
305+
ans = min(ans, 1 + dfs(j + 1))
306+
return ans
307+
308+
n = len(s)
309+
ans = dfs(0)
310+
dfs.cache_clear()
311+
return ans
312+
```
313+
314+
#### Java
315+
316+
```java
317+
class Solution {
318+
private int n;
319+
private char[] s;
320+
private Integer[] f;
321+
322+
public int minimumSubstringsInPartition(String s) {
323+
n = s.length();
324+
f = new Integer[n];
325+
this.s = s.toCharArray();
326+
return dfs(0);
327+
}
328+
329+
private int dfs(int i) {
330+
if (i >= n) {
331+
return 0;
332+
}
333+
if (f[i] != null) {
334+
return f[i];
335+
}
336+
int[] cnt = new int[26];
337+
int ans = n - i;
338+
int k = 0, m = 0;
339+
for (int j = i; j < n; ++j) {
340+
k += ++cnt[s[j] - 'a'] == 1 ? 1 : 0;
341+
m = Math.max(m, cnt[s[j] - 'a']);
342+
if (j - i + 1 == k * m) {
343+
ans = Math.min(ans, 1 + dfs(j + 1));
344+
}
345+
}
346+
return f[i] = ans;
347+
}
348+
}
349+
```
350+
351+
#### C++
352+
353+
```cpp
354+
class Solution {
355+
public:
356+
int minimumSubstringsInPartition(string s) {
357+
int n = s.size();
358+
int f[n];
359+
memset(f, -1, sizeof(f));
360+
auto dfs = [&](auto&& dfs, int i) -> int {
361+
if (i >= n) {
362+
return 0;
363+
}
364+
if (f[i] != -1) {
365+
return f[i];
366+
}
367+
f[i] = n - i;
368+
int cnt[26]{};
369+
int k = 0, m = 0;
370+
for (int j = i; j < n; ++j) {
371+
k += ++cnt[s[j] - 'a'] == 1 ? 1 : 0;
372+
m = max(m, cnt[s[j] - 'a']);
373+
if (j - i + 1 == k * m) {
374+
f[i] = min(f[i], 1 + dfs(dfs, j + 1));
375+
}
376+
}
377+
return f[i];
378+
};
379+
return dfs(dfs, 0);
380+
}
381+
};
382+
```
383+
384+
#### Go
385+
386+
```go
387+
func minimumSubstringsInPartition(s string) int {
388+
n := len(s)
389+
f := make([]int, n)
390+
for i := range f {
391+
f[i] = -1
392+
}
393+
var dfs func(int) int
394+
dfs = func(i int) int {
395+
if i >= n {
396+
return 0
397+
}
398+
if f[i] != -1 {
399+
return f[i]
400+
}
401+
cnt := [26]int{}
402+
f[i] = n - i
403+
k, m := 0, 0
404+
for j := i; j < n; j++ {
405+
x := int(s[j] - 'a')
406+
cnt[x]++
407+
if cnt[x] == 1 {
408+
k++
409+
}
410+
m = max(m, cnt[x])
411+
if j-i+1 == k*m {
412+
f[i] = min(f[i], 1+dfs(j+1))
413+
}
414+
}
415+
return f[i]
416+
}
417+
return dfs(0)
418+
}
419+
```
420+
421+
#### TypeScript
422+
423+
```ts
424+
function minimumSubstringsInPartition(s: string): number {
425+
const n = s.length;
426+
const f: number[] = Array(n).fill(-1);
427+
const dfs = (i: number): number => {
428+
if (i >= n) {
429+
return 0;
430+
}
431+
if (f[i] !== -1) {
432+
return f[i];
433+
}
434+
const cnt: number[] = Array(26).fill(0);
435+
f[i] = n - i;
436+
let [k, m] = [0, 0];
437+
for (let j = i; j < n; ++j) {
438+
const x = s.charCodeAt(j) - 97;
439+
k += ++cnt[x] === 1 ? 1 : 0;
440+
m = Math.max(m, cnt[x]);
441+
if (j - i + 1 === k * m) {
442+
f[i] = Math.min(f[i], 1 + dfs(j + 1));
443+
}
444+
}
445+
return f[i];
446+
};
447+
return dfs(0);
448+
}
449+
```
450+
451+
<!-- tabs:end -->
452+
453+
<!-- solution:end -->
454+
455+
<!-- solution:start -->
456+
457+
### 方法三:动态规划
458+
459+
我们可以将记忆化搜索转换为动态规划,定义状态 $f[i]$ 对前 $i$ 个字符进行分割的最少子字符串数量。初始时 $f[0] = 0$,其余 $f[i] = +\infty$ 或者 $f[i] = n$。
460+
461+
接下来我们枚举 $i$ 从 $0$ 到 $n-1$,对于每个 $i$,我们维护一个哈希表 $\textit{cnt}$,表示当前子字符串中每个字符出现的次数。另外,我们维护两个变量 $k$ 和 $m$ 分别表示当前子字符串中的字符种类数和出现次数最多的字符的出现次数。对于一个子串 $s[j..i]$,如果 $i-j+1 = m \times k$,那么这个子串就是一个平衡子串。此时我们可以从 $j$ 开始分割,那么 $f[i+1] = \min(f[i+1], f[j] + 1)$。
462+
463+
最终答案为 $f[n]$。
464+
465+
时间复杂度 $O(n^2)$,空间复杂度 $O(n + |\Sigma|)$。其中 $n$ 为字符串 $s$ 的长度,而 $|\Sigma|$ 表示字符集的大小,本题中 $|\Sigma| = 26$。
466+
467+
<!-- tabs:start -->
468+
469+
#### Python3
470+
471+
```python
472+
class Solution:
473+
def minimumSubstringsInPartition(self, s: str) -> int:
474+
n = len(s)
475+
f = [inf] * (n + 1)
476+
f[0] = 0
477+
for i in range(n):
478+
cnt = defaultdict(int)
479+
m = 0
480+
for j in range(i, -1, -1):
481+
cnt[s[j]] += 1
482+
m = max(m, cnt[s[j]])
483+
if i - j + 1 == len(cnt) * m:
484+
f[i + 1] = min(f[i + 1], f[j] + 1)
485+
return f[n]
486+
```
487+
488+
#### Java
489+
490+
```java
491+
class Solution {
492+
public int minimumSubstringsInPartition(String s) {
493+
int n = s.length();
494+
char[] cs = s.toCharArray();
495+
int[] f = new int[n + 1];
496+
Arrays.fill(f, n);
497+
f[0] = 0;
498+
for (int i = 0; i < n; ++i) {
499+
int[] cnt = new int[26];
500+
int k = 0, m = 0;
501+
for (int j = i; j >= 0; --j) {
502+
k += ++cnt[cs[j] - 'a'] == 1 ? 1 : 0;
503+
m = Math.max(m, cnt[cs[j] - 'a']);
504+
if (i - j + 1 == k * m) {
505+
f[i + 1] = Math.min(f[i + 1], 1 + f[j]);
506+
}
507+
}
508+
}
509+
return f[n];
510+
}
511+
}
512+
```
513+
514+
#### C++
515+
516+
```cpp
517+
class Solution {
518+
public:
519+
int minimumSubstringsInPartition(string s) {
520+
int n = s.size();
521+
vector<int> f(n + 1, n);
522+
f[0] = 0;
523+
for (int i = 0; i < n; ++i) {
524+
int cnt[26]{};
525+
int k = 0, m = 0;
526+
for (int j = i; ~j; --j) {
527+
k += ++cnt[s[j] - 'a'] == 1;
528+
m = max(m, cnt[s[j] - 'a']);
529+
if (i - j + 1 == k * m) {
530+
f[i + 1] = min(f[i + 1], f[j] + 1);
531+
}
532+
}
533+
}
534+
return f[n];
535+
}
536+
};
537+
```
538+
539+
#### Go
540+
541+
```go
542+
func minimumSubstringsInPartition(s string) int {
543+
n := len(s)
544+
f := make([]int, n+1)
545+
for i := range f {
546+
f[i] = n
547+
}
548+
f[0] = 0
549+
for i := 0; i < n; i++ {
550+
cnt := [26]int{}
551+
k, m := 0, 0
552+
for j := i; j >= 0; j-- {
553+
x := int(s[j] - 'a')
554+
cnt[x]++
555+
if cnt[x] == 1 {
556+
k++
557+
}
558+
m = max(m, cnt[x])
559+
if i-j+1 == k*m {
560+
f[i+1] = min(f[i+1], 1+f[j])
561+
}
562+
}
563+
}
564+
return f[n]
565+
}
566+
```
567+
568+
#### TypeScript
569+
570+
```ts
571+
function minimumSubstringsInPartition(s: string): number {
572+
const n = s.length;
573+
const f: number[] = Array(n + 1).fill(n);
574+
f[0] = 0;
575+
for (let i = 0; i < n; ++i) {
576+
const cnt: number[] = Array(26).fill(0);
577+
let [k, m] = [0, 0];
578+
for (let j = i; ~j; --j) {
579+
const x = s.charCodeAt(j) - 97;
580+
k += ++cnt[x] === 1 ? 1 : 0;
581+
m = Math.max(m, cnt[x]);
582+
if (i - j + 1 === k * m) {
583+
f[i + 1] = Math.min(f[i + 1], 1 + f[j]);
584+
}
585+
}
586+
}
587+
return f[n];
588+
}
589+
```
590+
591+
<!-- tabs:end -->
592+
593+
<!-- solution:end -->
594+
279595
<!-- problem:end -->

0 commit comments

Comments
 (0)