2478. 完美分割的方案数
2478. 完美分割的方案数
难度困难
给你一个字符串 s
,每个字符是数字 '1'
到 '9'
,再给你两个整数 k
和 minLength
。
如果对 s
的分割满足以下条件,那么我们认为它是一个 完美 分割:
s
被分成k
段互不相交的子字符串。- 每个子字符串长度都 至少 为
minLength
。 - 每个子字符串的第一个字符都是一个 质数 数字,最后一个字符都是一个 非质数 数字。质数数字为
'2'
,'3'
,'5'
和'7'
,剩下的都是非质数数字。
请你返回 s
的 完美 分割数目。由于答案可能很大,请返回答案对 109 + 7
取余 后的结果。
一个 子字符串 是字符串中一段连续字符串序列。
示例 1:
输入: s = “23542185131”, k = 3, minLength = 2 输出: 3 解释: 存在 3 种完美分割方案: “2354 | 218 | 5131” “2354 | 21851 | 31” “2354218 | 51 | 31”
示例 2:
输入: s = “23542185131”, k = 3, minLength = 3 输出: 1 解释: 存在一种完美分割方案:“2354 | 218 | 5131” 。
示例 3:
输入: s = “3312958”, k = 3, minLength = 1 输出: 1 解释: 存在一种完美分割方案:“331 | 29 | 58” 。
提示:
1 <= k, minLength <= s.length <= 1000
s
每个字符都为数字'1'
到'9'
之一。
函数签名:
func beautifulPartitions(s string, k int, minLength int) int
分析
动态规划
问题规模可以缩小。划分为 k 段子串,如果去除最后一段,算出剩余的字符串划分为 k-1 段的数量,那么可以推出将整个字符串划分为 k 个子串的数量。
假设用dp[i][k]
表示将字符串前 i 个字符分割成k段,满足题意的解法的数量。
要求dp[i][k]
就需要先找到一个更早的状态dp[j][k-1]
,当然字符串s[j+1:i]
需满足题意。这样dp[i][k] += dp[j][k-1]
。遍历所有可能的j,就能求出答案。
func beautifulPartitions(s string, k int, minLength int) int {
const mod = 1e9+7
n := len(s)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, k+1)
}
dp[0][0] = 1
for i := minLength; i <= n; i++ {
if isPrime(s[i-1]) {
continue
}
for j := 0; j <= i-minLength; j++ {
if !isPrime(s[j]) {
continue
}
for k1 := 1; k1 <= k; k1++ {
dp[i][k1] = (dp[i][k1]+dp[j][k1-1]) % mod
}
}
}
return dp[n][k]
}
func isPrime(b byte) bool {
return b == '2' || b == '3' || b == '5' || b == '7'
}
时间复杂度:O(k*n^2)
, 空间复杂度 O(n*k)
。
优化后的动态规划
TODO