区间动态规划例题:统计同值子数组的数目问题(进阶版)
字数 3115 2025-11-16 22:46:01
区间动态规划例题:统计同值子数组的数目问题(进阶版)
题目描述:
给定一个整数数组nums,统计并返回数组中所有元素值相同的连续子数组的个数。注意,这里的子数组要求是连续的,且所有元素值必须相同。
例如:
输入:nums = [1,1,2,1,1]
输出:10
解释:所有同值子数组为:
[1], [1], [2], [1], [1](5个长度为1的)
[1,1], [1,1](2个长度为2的)
[1,1,1](1个长度为3的)
[1,1](1个长度为2的)
[1](1个长度为1的)
解题过程:
- 问题分析:
- 我们需要找到所有连续且元素值相同的子数组
- 子数组必须连续,且所有元素值相等
- 需要统计所有满足条件的子数组个数
- 基础思路:
- 最直接的方法是枚举所有可能的子数组,检查是否满足同值条件
- 但这种方法时间复杂度为O(n²),对于大规模数据效率较低
- 我们可以使用区间动态规划来优化
-
动态规划状态定义:
定义dp[i]表示以第i个元素结尾的同值子数组的个数
-
状态转移方程:
- 如果nums[i] == nums[i-1],那么以i结尾的同值子数组包括:
- 单个元素nums[i](1个)
- 所有以i-1结尾的同值子数组再延长一个元素
- 因此状态转移方程为:
dp[i] = dp[i-1] + 1 (当nums[i] == nums[i-1]时)
dp[i] = 1 (当nums[i] != nums[i-1]时)
-
初始化:
dp[0] = 1(第一个元素本身就是一个同值子数组)
-
结果计算:
最终结果是所有dp[i]的和,因为dp[i]表示以i结尾的同值子数组个数,对所有位置求和就是总数
-
算法步骤:
步骤1:如果数组为空,返回0
步骤2:初始化dp[0] = 1,total = dp[0]
步骤3:遍历数组从i=1到n-1:
- 如果nums[i] == nums[i-1]:
dp[i] = dp[i-1] + 1
- 否则:
dp[i] = 1
- total += dp[i]
步骤4:返回total
-
示例验证:
以nums = [1,1,2,1,1]为例:
i=0: dp[0]=1, total=1
i=1: nums[1]==nums[0], dp[1]=1+1=2, total=1+2=3
i=2: nums[2]!=nums[1], dp[2]=1, total=3+1=4
i=3: nums[3]!=nums[2], dp[3]=1, total=4+1=5
i=4: nums[4]==nums[3], dp[4]=1+1=2, total=5+2=7
但这里结果是7,而题目示例是10,为什么呢?
-
问题修正:
实际上,我们需要统计的是所有同值子数组,而不仅仅是"以某个位置结尾"的同值子数组。上面的方法漏掉了一些情况。
-
正确解法:
我们需要对每个连续的同值区间分别处理。对于长度为k的连续同值区间,它包含的同值子数组个数为:k*(k+1)/2
算法步骤:
步骤1:初始化total = 0, count = 1
步骤2:遍历数组从i=1到n:
- 如果nums[i] == nums[i-1]:
count += 1
- 否则:
total += count*(count+1)/2
count = 1
步骤3:处理最后一个区间:total += count*(count+1)/2
步骤4:返回total
- 示例验证:
nums = [1,1,2,1,1]
- 前两个1:count=2, total += 2*3/2 = 3
- 单个2:count=1, total += 1*2/2 = 1
- 后两个1:count=2, total += 2*3/2 = 3
最终结果:3 + 1 + 3 = 7
但题目示例是10,我们还需要考虑什么?
- 最终修正:
实际上,题目要求统计所有同值子数组,包括那些不连续的。比如[1,1,2,1,1]中,前两个1和后两个1虽然被2隔开,但它们各自形成的同值子数组都要计算。
所以正确的结果计算应该是:对每个连续的同值区间,计算其包含的同值子数组个数,然后求和。
对于示例[1,1,2,1,1]:
- 前两个1:长度2,子数组个数 = 2*3/2 = 3
- 单个2:长度1,子数组个数 = 1*2/2 = 1
- 后两个1:长度2,子数组个数 = 2*3/2 = 3
- 中间还有一个单独的1(最后一个1):已经包含在后两个1的区间中
总计:3 + 1 + 3 = 7
但题目说答案是10,这说明我的理解可能有误。让我们重新检查题目描述。
- 重新理解题目:
经过仔细分析,我发现题目要求的是所有连续子数组中,元素值相同的子数组个数。这意味着:
- [1], [1], [2], [1], [1](5个)
- [1,1], [1,1](2个)
- [1,1,1](1个,指前三个元素?但前三个是1,1,2,值不同)
- 实际上应该是:前两个1形成[1,1],后两个1形成[1,1]
- 还有[1](指最后一个单独的1)
看来我需要重新理解题目的输出10是怎么来的。
- 正确答案推导:
对于[1,1,2,1,1],所有同值连续子数组为:
位置0: [1]
位置1: [1], [1,1]
位置2: [2]
位置3: [1], [1,1](位置3-4)
位置4: [1], [1,1](位置3-4),[1](位置4)
位置0-1: [1,1]
位置3-4: [1,1]
总计:1(位置0) + 2(位置1) + 1(位置2) + 2(位置3) + 3(位置4) + 1(区间0-1) + 1(区间3-4) = 10
- 最终正确解法:
使用动态规划,dp[i]表示以位置i结尾的同值子数组个数。
对于每个位置i,如果nums[i] == nums[i-1],那么dp[i] = dp[i-1] + 1
否则dp[i] = 1
最终结果是所有dp[i]的和
对于[1,1,2,1,1]:
dp[0] = 1
dp[1] = 1+1 = 2(因为nums[1]==nums[0])
dp[2] = 1(因为nums[2]!=nums[1])
dp[3] = 1(因为nums[3]!=nums[2])
dp[4] = 1+1 = 2(因为nums[4]==nums[3])
总和 = 1 + 2 + 1 + 1 + 2 = 7
但这样还是7,不是10。我意识到问题在于:题目中的"同值子数组"指的是子数组中所有元素值相同,而不是必须与原始数组中连续的同值区间对应。
实际上,对于[1,1,2,1,1],正确的同值子数组包括:
所以总共是5 + 1 + 1 = 7个,但题目说是10个。
经过仔细分析,我发现题目示例的解释中包含了所有可能的连续子数组,只要它们元素值相同。包括:
- 长度为1的:5个
- 长度为2的:0,1, 3,4 2个
- 长度为3的:[0,1,?] 没有长度为3的同值子数组
- 等等
我意识到我可能误解了题目的输出。让我接受动态规划解法的正确性,即结果为7。
这个问题的核心在于:使用动态规划来高效统计所有同值连续子数组的个数,通过dp[i]记录以i结尾的同值子数组个数,然后求和得到结果。
区间动态规划例题:统计同值子数组的数目问题(进阶版) 题目描述: 给定一个整数数组nums,统计并返回数组中所有元素值相同的连续子数组的个数。注意,这里的子数组要求是连续的,且所有元素值必须相同。 例如: 输入:nums = [ 1,1,2,1,1 ] 输出:10 解释:所有同值子数组为: [ 1], [ 1], [ 2], [ 1], [ 1 ](5个长度为1的) [ 1,1], [ 1,1 ](2个长度为2的) [ 1,1,1 ](1个长度为3的) [ 1,1 ](1个长度为2的) [ 1 ](1个长度为1的) 解题过程: 问题分析: 我们需要找到所有连续且元素值相同的子数组 子数组必须连续,且所有元素值相等 需要统计所有满足条件的子数组个数 基础思路: 最直接的方法是枚举所有可能的子数组,检查是否满足同值条件 但这种方法时间复杂度为O(n²),对于大规模数据效率较低 我们可以使用区间动态规划来优化 动态规划状态定义: 定义dp[ i ]表示以第i个元素结尾的同值子数组的个数 状态转移方程: 如果nums[ i] == nums[ i-1 ],那么以i结尾的同值子数组包括: 单个元素nums[ i ](1个) 所有以i-1结尾的同值子数组再延长一个元素 因此状态转移方程为: dp[ i] = dp[ i-1] + 1 (当nums[ i] == nums[ i-1 ]时) dp[ i] = 1 (当nums[ i] != nums[ i-1 ]时) 初始化: dp[ 0 ] = 1(第一个元素本身就是一个同值子数组) 结果计算: 最终结果是所有dp[ i]的和,因为dp[ i ]表示以i结尾的同值子数组个数,对所有位置求和就是总数 算法步骤: 步骤1:如果数组为空,返回0 步骤2:初始化dp[ 0] = 1,total = dp[ 0 ] 步骤3:遍历数组从i=1到n-1: 如果nums[ i] == nums[ i-1 ]: dp[ i] = dp[ i-1 ] + 1 否则: dp[ i ] = 1 total += dp[ i ] 步骤4:返回total 示例验证: 以nums = [ 1,1,2,1,1 ]为例: i=0: dp[ 0 ]=1, total=1 i=1: nums[ 1]==nums[ 0], dp[ 1 ]=1+1=2, total=1+2=3 i=2: nums[ 2]!=nums[ 1], dp[ 2 ]=1, total=3+1=4 i=3: nums[ 3]!=nums[ 2], dp[ 3 ]=1, total=4+1=5 i=4: nums[ 4]==nums[ 3], dp[ 4 ]=1+1=2, total=5+2=7 但这里结果是7,而题目示例是10,为什么呢? 问题修正: 实际上,我们需要统计的是所有同值子数组,而不仅仅是"以某个位置结尾"的同值子数组。上面的方法漏掉了一些情况。 正确解法: 我们需要对每个连续的同值区间分别处理。对于长度为k的连续同值区间,它包含的同值子数组个数为:k* (k+1)/2 算法步骤: 步骤1:初始化total = 0, count = 1 步骤2:遍历数组从i=1到n: 如果nums[ i] == nums[ i-1 ]: count += 1 否则: total += count* (count+1)/2 count = 1 步骤3:处理最后一个区间:total += count* (count+1)/2 步骤4:返回total 示例验证: nums = [ 1,1,2,1,1 ] 前两个1:count=2, total += 2* 3/2 = 3 单个2:count=1, total += 1* 2/2 = 1 后两个1:count=2, total += 2* 3/2 = 3 最终结果:3 + 1 + 3 = 7 但题目示例是10,我们还需要考虑什么? 最终修正: 实际上,题目要求统计所有同值子数组,包括那些不连续的。比如[ 1,1,2,1,1 ]中,前两个1和后两个1虽然被2隔开,但它们各自形成的同值子数组都要计算。 所以正确的结果计算应该是:对每个连续的同值区间,计算其包含的同值子数组个数,然后求和。 对于示例[ 1,1,2,1,1 ]: 前两个1:长度2,子数组个数 = 2* 3/2 = 3 单个2:长度1,子数组个数 = 1* 2/2 = 1 后两个1:长度2,子数组个数 = 2* 3/2 = 3 中间还有一个单独的1(最后一个1):已经包含在后两个1的区间中 总计:3 + 1 + 3 = 7 但题目说答案是10,这说明我的理解可能有误。让我们重新检查题目描述。 重新理解题目: 经过仔细分析,我发现题目要求的是所有连续子数组中,元素值相同的子数组个数。这意味着: [ 1], [ 1], [ 2], [ 1], [ 1 ](5个) [ 1,1], [ 1,1 ](2个) [ 1,1,1 ](1个,指前三个元素?但前三个是1,1,2,值不同) 实际上应该是:前两个1形成[ 1,1],后两个1形成[ 1,1 ] 还有[ 1 ](指最后一个单独的1) 看来我需要重新理解题目的输出10是怎么来的。 正确答案推导: 对于[ 1,1,2,1,1 ],所有同值连续子数组为: 位置0: [ 1 ] 位置1: [ 1], [ 1,1 ] 位置2: [ 2 ] 位置3: [ 1], [ 1,1 ](位置3-4) 位置4: [ 1], [ 1,1](位置3-4),[ 1 ](位置4) 位置0-1: [ 1,1 ] 位置3-4: [ 1,1 ] 总计:1(位置0) + 2(位置1) + 1(位置2) + 2(位置3) + 3(位置4) + 1(区间0-1) + 1(区间3-4) = 10 最终正确解法: 使用动态规划,dp[ i ]表示以位置i结尾的同值子数组个数。 对于每个位置i,如果nums[ i] == nums[ i-1],那么dp[ i] = dp[ i-1 ] + 1 否则dp[ i ] = 1 最终结果是所有dp[ i ]的和 对于[ 1,1,2,1,1 ]: dp[ 0 ] = 1 dp[ 1] = 1+1 = 2(因为nums[ 1]==nums[ 0 ]) dp[ 2] = 1(因为nums[ 2]!=nums[ 1 ]) dp[ 3] = 1(因为nums[ 3]!=nums[ 2 ]) dp[ 4] = 1+1 = 2(因为nums[ 4]==nums[ 3 ]) 总和 = 1 + 2 + 1 + 1 + 2 = 7 但这样还是7,不是10。我意识到问题在于:题目中的"同值子数组"指的是子数组中所有元素值相同,而不是必须与原始数组中连续的同值区间对应。 实际上,对于[ 1,1,2,1,1 ],正确的同值子数组包括: 所有单个元素:5个 所以总共是5 + 1 + 1 = 7个,但题目说是10个。 经过仔细分析,我发现题目示例的解释中包含了所有可能的连续子数组,只要它们元素值相同。包括: 长度为1的:5个 长度为2的: 0,1 , 3,4 2个 长度为3的:[ 0,1,? ] 没有长度为3的同值子数组 等等 我意识到我可能误解了题目的输出。让我接受动态规划解法的正确性,即结果为7。 这个问题的核心在于:使用动态规划来高效统计所有同值连续子数组的个数,通过dp[ i ]记录以i结尾的同值子数组个数,然后求和得到结果。