Leetcode: 2478. Number of Beautiful Partitions
Solution
Be
class Solution: def beautifulPartitions(self, s: str, k: int, minLength: int) -> int: N = len(s) P = "2357" if s[0] not in P or s[-1] in P: return 0 @cache def dfs(i, k, level=0): if i > N or k < 0: return 0 if i == N: return 1 if k == 0 else 0 ans = dfs(i + 1, k, level + 1) if s[i - 1] not in P and s[i] in P: ans += dfs(i + minLength, k - 1, level + 1) return ans % (10**9 + 7) return dfs(minLength, k - 1)
Solution
- Can we derive an invariant based on the smallest possible examples? If
starts is a non-prime or ends with a prime, then the solution is always 0. - Can we pre-process the input in a way to make easy to solve the problem? We want to partition
only in positions where is a non-prime and is a prime. So, we can create with indexes where valid partitions can start. Now, the problem becomes find partitions on indexes in of length at least . The first idea that comes to mind is using Dynamic Programming to solve the problem. Be
the number of ways to partitions in partitions. Be indexes where is a valid partition. It is not difficult to see how to compute each sub problem in linear time. This solution won't make in time, since the complexity is .Can we break-down the problem in small and easily to solve parts? The problem with the above recurrence is that it takes linear time to compute each sub problem. To improve it, we can break-down the third-case in other Dynamic Programming that depends on
. Be the number of beautiful partitions starting on $starts[i], starts[i+1], starts[i+2], …$. We have that . Note that there are possible states for , but we can compute each one in . Now, we can re-write in terms of and the time complexity to solve the problem becomes with space . The log comes from the Binary Search to find the next valid position to start a partition after .
class Solution: def beautifulPartitions(self, s: str, k: int, minLength: int) -> int: N = len(s) MOD = 10**9 + 7 primes = "2357" if s[0] not in primes or s[-1] in primes: return 0 starts = [] for i, a, b in zip(range(N), s, s[1:]): if a not in primes and b in primes: starts.append(i + 1) starts.append(N) @cache def dfs2(i, k): if i == len(starts): return 0 return (dfs1(starts[i], k) + dfs2(i + 1, k)) % MOD @cache def dfs1(i, k): if i == N: return 1 if k == 0 else 0 if k < 0: return 0 return dfs2(bisect_left(starts, i + minLength), k - 1) % MOD return dfs1(0, k)