区间动态规划例题:统计同值子数组的数目问题(进阶版)
字数 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的)

解题过程:

  1. 问题分析:
  • 我们需要找到所有连续且元素值相同的子数组
  • 子数组必须连续,且所有元素值相等
  • 需要统计所有满足条件的子数组个数
  1. 基础思路:
  • 最直接的方法是枚举所有可能的子数组,检查是否满足同值条件
  • 但这种方法时间复杂度为O(n²),对于大规模数据效率较低
  • 我们可以使用区间动态规划来优化
  1. 动态规划状态定义:
    定义dp[i]表示以第i个元素结尾的同值子数组的个数

  2. 状态转移方程:

  • 如果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]时)
  1. 初始化:
    dp[0] = 1(第一个元素本身就是一个同值子数组)

  2. 结果计算:
    最终结果是所有dp[i]的和,因为dp[i]表示以i结尾的同值子数组个数,对所有位置求和就是总数

  3. 算法步骤:
    步骤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
  4. 示例验证:
    以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,为什么呢?

  1. 问题修正:
    实际上,我们需要统计的是所有同值子数组,而不仅仅是"以某个位置结尾"的同值子数组。上面的方法漏掉了一些情况。

  2. 正确解法:
    我们需要对每个连续的同值区间分别处理。对于长度为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
  1. 示例验证:
    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,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], [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,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

  1. 最终正确解法:
    使用动态规划,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结尾的同值子数组个数,然后求和得到结果。

区间动态规划例题:统计同值子数组的数目问题(进阶版) 题目描述: 给定一个整数数组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结尾的同值子数组个数,然后求和得到结果。