3144.分割字符频率相等的最少子字符串

【LetMeFly】3144.分割字符频率相等的最少子字符串:动态规划+哈希表

力扣题目链接:https://leetcode.cn/problems/minimum-substring-partition-of-equal-character-frequency/

给你一个字符串 s ,你需要将它分割成一个或者更多的 平衡 子字符串。比方说,s == "ababcc" 那么 ("abab", "c", "c") ,("ab", "abc", "c") 和 ("ababcc") 都是合法分割,但是 ("a", "bab", "cc") ,("aba", "bc", "c") 和 ("ab", "abcc") 不是,不平衡的子字符串用粗体表示。

请你返回 s 最少 能分割成多少个平衡子字符串。

注意:一个 平衡 字符串指的是字符串中所有字符出现的次数都相同。

 

示例 1:

输入:s = "fabccddg"

输出:3

解释:

我们可以将 s 分割成 3 个子字符串:("fab, "ccdd", "g") 或者 ("fabc", "cd", "dg") 。

示例 2:

输入:s = "abababaccddb"

输出:2

解释:

我们可以将 s 分割成 2 个子字符串:("abab", "abaccddb") 。

 

提示:

  • 1 <= s.length <= 1000
  • s 只包含小写英文字母。

解题方法:动态规划

使用一个动态规划数组$dp$,其中$dp[i]$代表字符串$s$的前$i$个字符最少能分割成多少个平衡子字符串。初始值除了$dp[0]=0$外都是“无穷大”。

接着使用下标$i$从前往后遍历字符串。假设字符串的$j$到$i$子串是平衡字符串,那么有$dp[i + 1] = \min (dp[i + 1], dp[j] + 1)$。

对于一个下标$i$,如何判断是否存在一个$j\lt i$,使得$j$到$i$的子串是平衡字符串呢?我们可以使用$j$从$i$往前开始遍历字符串,并使用一个哈希表统计每个字符出现的次数,再使用两个变量$types$和$maxTimes$分别统计$j$到$i$的子串中字符种类数和字符最大出现次数。如果$字符种类数\times 字符最大出现次数=子串长度$,就说明子串是平衡字符串(每个字符出现次数相等,都是$maxTimes$次)。

  • 时间复杂度$O(len(s)^2)$
  • 空间复杂度$O(len(s) + C)$,其中$C$是字符种类数,$C=26$

AC代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int minimumSubstringsInPartition(string& s) {
vector<int> dp(s.size() + 1, 1000000);
dp[0] = 0;
unordered_map<char, int> ma;
for (int i = 0; i < s.size(); i++) {
ma.clear();
int types = 0, maxTimes = 0;
for (int j = i; j >= 0; j--) {
maxTimes = max(maxTimes, ++ma[s[j]]);
if (ma[s[j]] == 1) {
types++;
}
if (maxTimes * types == i - j + 1) {
dp[i + 1] = min(dp[i + 1], dp[j] + 1);
}
}
}
return dp.back();
}
};

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

func min(a int, b int) int {
if (a > b) {
return b
}
return a
}

func max(a int, b int) int {
if (a < b) {
return b
}
return a
}

func minimumSubstringsInPartition(s string) int {
dp := make([]int, len(s) + 1)
for i := range dp {
dp[i] = 100000
}
dp[0] = 0
for i := range s {
ma := map[byte]int{}
types, maxTimes := 0, 0
for j := i; j >= 0; j-- {
ma[s[j]]++
maxTimes = max(maxTimes, ma[s[j]])
if ma[s[j]] == 1 {
types++
}
if types * maxTimes == i - j + 1 {
dp[i + 1] = min(dp[i + 1], dp[j] + 1)
}
}
}
return dp[len(s)]
}


Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;

class Solution {
public int minimumSubstringsInPartition(String s) {
int[] dp = new int[s.length() + 1];
Arrays.fill(dp, 100000);
dp[0] = 0;
Map<Character, Integer> ma = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
ma.clear();
int types = 0, maxTimes = 0;
for (int j = i; j >= 0; j--) {
int originalTimes = ma.getOrDefault(s.charAt(j), 0);
ma.put(s.charAt(j), originalTimes + 1);
maxTimes = Math.max(maxTimes, originalTimes + 1);
if (originalTimes == 0) {
types++;
}
if (maxTimes * types == i - j + 1) {
dp[i + 1] = Math.min(dp[i + 1], dp[j] + 1);
}
}
}
return dp[s.length()];
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from collections import defaultdict

class Solution:
def minimumSubstringsInPartition(self, s: str) -> int:
dp = [100000] * (len(s) + 1)
dp[0] = 0
ma = defaultdict(int)
for i in range(len(s)):
ma.clear()
types, maxTimes = 0, 0
for j in range(i, -1, -1):
ma[s[j]] += 1
maxTimes = max(maxTimes, ma[s[j]])
if ma[s[j]] == 1:
types += 1
if maxTimes * types == i - j + 1:
dp[i + 1] = min(dp[i + 1], dp[j] + 1)
return dp[-1]

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

Tisfy:https://letmefly.blog.csdn.net/article/details/141652980


3144.分割字符频率相等的最少子字符串
https://blog.letmefly.xyz/2024/08/28/LeetCode 3144.分割字符频率相等的最少子字符串/
作者
Tisfy
发布于
2024年8月28日
许可协议