Number of Beautiful Partitions

Number of Beautiful Partitions

You are given a string s that consists of the digits '1' to '9' and two integers k and minLength.

A partition of s is called beautiful if:

  • s is partitioned into k non-intersecting substrings.
  • Each substring has a length of at least minLength .
  • Each substring starts with a prime digit and ends with a non-prime digit. Prime digits are '2' , '3' , '5' , and '7' , and the rest of the digits are non-prime.

Return the number of beautiful partitions of s . Since the answer may be very large, return it modulo ${10}^{9}+7$.

A substring is a contiguous sequence of characters within a string.

Example 1:

Input: s = "23542185131", k = 3, minLength = 2
Output: 3
Explanation: There exists three ways to create a beautiful partition:
"2354 | 218 | 5131"
"2354 | 21851 | 31"
"2354218 | 51 | 31"

Example 2:

Input: s = "23542185131", k = 3, minLength = 3
Output: 1
Explanation: There exists one way to create a beautiful partition: "2354 | 218 | 5131".

Example 3:

Input: s = "3312958", k = 3, minLength = 1
Output: 1
Explanation: There exists one way to create a beautiful partition: "331 | 29 | 58".

Constraints:

$1 \leq k, minLength \leq s.length \leq 1000$
s consists of the digits '1' to '9' .

 

解题思路

  这题很容易想到动态规划,不过如果直接暴力dp的话会超时,因此还需要进行优化,比赛的时候调了$1$个多小时都没有调出来,说白了就是不熟练。

  定义状态$f(i, j)$表示所有从前$i$字符中分成$j$组的合法方案的数量。状态转移方程就是$$f(i, j) = \sum\limits_{u = \text{len} \times (j - 1) + 1}^{i - \text{len} + 1}{f(u-1,j-1)}$$其中要保证$s_i$不为质数,并且$s_u$要为质数。$u$是第$j$组的左端点,$i$是第$j$组的右端点。

  因此就很容易写出TLE代码,时间复杂度为$O(n^2 \times k)$。

 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4     
 5     int beautifulPartitions(string s, int k, int minLength) {
 6         int n = s.size();
 7         vector<vector<int>> f(n + 1, vector<int>(k + 1));
 8         f[0][0] = 1;
 9         unordered_set<int> st({2, 3, 5, 7});
10         for (int i = 1; i <= n; i++) {
11             if (!st.count(s[i - 1] - '0')) {
12                 for (int j = 1; j <= k; j++) {
13                     for (int u = minLength * (j - 1) + 1; u + minLength - 1 <= i; u++) {
14                         if (st.count(s[u - 1] - '0')) f[i][j] = (f[i][j] + f[u - 1][j - 1]) % mod;
15                     }
16                 }
17             }
18         }
19         return f[n][k];
20     }
21 };

  现在就要对其进行优化,对于第$3$个循环,可以发现如果我们固定$j$不变,而每次增加$i$,那么$u$的枚举范围$[\text{len} \times (j-1) + 1, i - \text{len} + 1]$的左端点总是固定不变的,而右端点每次会加$1$,这意味着我们总是重复枚举前面的固定不变的部分,因此我们可以考虑开个变量$\text{cnt}$来累加前面固定的部分,这样就不用每次从头开始枚举了。当时比赛就是卡在$\text{cnt}$所要累加的值。其实看状态转移方程就可以发现,$f(i, j)$每次都是加上$f(u-1,j-1)$,而第一维是变化的量,对比于$i-1$,当枚举到$i$时就只多加了一个$f(i + 1 - \text{len} + 1, j-1)$。因此当枚举到$i$,$\text{cnt}$就累加$f(i - \text{len} + 1, j-1)$就可以了(前提是$s_{i - \text{len} + 1}$为质数)。关键是要看到前后两次是哪个量发生了变化。

  AC代码如下,时间复杂度为$O(n \times k)$:

 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4 
 5     int beautifulPartitions(string s, int k, int minLength) {
 6         int n = s.size();
 7         vector<vector<int>> f(n + 1, vector<int>(k + 1));
 8         f[0][0] = 1;
 9         unordered_set<int> st({2, 3, 5, 7});
10         for (int j = 1; j <= k; j++) {
11             int cnt = 0;
12             for (int i = minLength * j; i <= n; i++) {
13                 if (st.count(s[i - minLength] - '0')) cnt = (cnt + f[i - minLength][j - 1]) % mod;
14                 if (!st.count(s[i - 1] - '0')) f[i][j] = cnt;
15             }
16         }
17         return f[n][k];
18     }
19 };

  当然,可以改变$i$和$j$的枚举顺序,这里就定义$f(j, i)$表示所有从前$i$字符中分成$j$组的合法方案的数量(和上面的定义一样),这种写法会比上面的快一些(涉及到底层)。

  AC代码如下:

 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4 
 5     int beautifulPartitions(string s, int k, int minLength) {
 6         int n = s.size();
 7         vector<vector<int>> f(k + 1, vector<int>(n + 1));
 8         f[0][0] = 1;
 9         unordered_set<int> st({2, 3, 5, 7});
10         for (int j = 1; j <= k; j++) {
11             int cnt = 0;
12             for (int i = minLength * j; i <= n; i++) {
13                 if (st.count(s[i - minLength] - '0')) cnt = (cnt + f[j - 1][i - minLength]) % mod;
14                 if (!st.count(s[i - 1] - '0')) f[j][i] = cnt;
15             }
16         }
17         return f[k][n];
18     }
19 };
posted @ 2022-11-20 15:38  onlyblues  阅读(20)  评论(0编辑  收藏  举报
Web Analytics