LC 100136. 统计好分割方案的数目
https://leetcode.cn/problems/count-the-number-of-good-partitions/description/
如果是用前向搜索的话,好像枚举状态会比较多,所以比较适合从后往前搜索。
我这里写个一个DP状态方程,看的出来似乎是需要保持另外一个数组来进行累计。
- \(dp[i] = dp[j+1] + .. + dp[n-1]. st. nums[i] = nums[j]\)
- \(acc[i] = acc[i] + acc[i+1] + ... + acc[n-1]\)
对于第一个条件,还有一个附加条件,就是这个 i 必须是最开始的下标,比如 `[1,5,1,5,8]` 为例:
- 对于 i=0 来说,它的last其实是 i=3 这个下标
- 但是对于 i=1 来说,其实它并不是有效的前缀,所以这里 `last[i]=-1`.
- 这个在状态转移的时候需要稍微判断一下。
如果我们要求解到有效的 `last` 的话,需要多轮枚举。还是以 `[1,5,1,5,8]` 为例:
- i=0, 它的直接last是 i=2
- 但是中间i=1包含了5, 而它的last是i=3
- 所以i=0的last应该是i=3.
class Solution:
def numberOfGoodPartitions(self, nums: List[int]) -> int:
# dp[i] = dp[j+1] + .. + dp[n-1] where nums[i] = nums[j].
# acc[i] = acc[i] + acc[i+1] + ... + acc[n-1]
n = len(nums)
direct = {}
for i in range(n):
direct[nums[i]] = i
last = [-1] * n
visited = [0] * n
for i in range(n):
if visited[i]: continue
j = i
end = direct[nums[i]]
while j < end:
end = max(direct[nums[j]], end)
j += 1
last[i] = end
for k in range(i, j + 1):
visited[k] = 1
MOD = 10 ** 9 + 7
# print(last)
dp = [0] * n
acc = [0] * (n + 1)
acc[-1] = 1
for i in reversed(range(n)):
p = last[i]
if p == -1:
dp[i] = 0
else:
dp[i] = acc[p + 1]
acc[i] = (acc[i + 1] + dp[i]) % MOD
return dp[0]