leetcode
leetcode 1751 ~ 1800
统计特殊子序列的数目

统计特殊子序列的数目

难度:

标签:

题目描述

代码结果

运行时间: 154 ms, 内存: 20.2 MB


/*
 * 思路:
 * 使用动态规划来解决这个问题。定义三个变量 count0、count1 和 count2 分别表示以 0、1 和 2 结尾的特殊子序列的数量。
 * 遍历数组 nums,根据当前的数字更新这三个变量。
 * 1. 如果当前数字是 0,那么 count0 += 1(每个 0 都可以单独成为一个特殊序列的一部分)。
 * 2. 如果当前数字是 1,那么 count1 += count0(每个 1 可以和之前的所有 0 组成一个特殊序列)。
 * 3. 如果当前数字是 2,那么 count2 += count1(每个 2 可以和之前的所有 1 组成一个特殊序列)。
 * 最终答案就是 count2 对 10^9 + 7 取余。
 */
import java.util.stream.*;

public class Solution {
    public int countSpecialSubsequences(int[] nums) {
        int MOD = 1000000007;
        long[] counts = new long[3];
        for (int num : nums) {
            if (num == 0) {
                counts[0] = (counts[0] + 1) % MOD;
            } else if (num == 1) {
                counts[1] = (counts[1] + counts[0]) % MOD;
            } else if (num == 2) {
                counts[2] = (counts[2] + counts[1]) % MOD;
            }
        }
        return (int) (counts[2] % MOD);
    }
}

解释

方法:

该题解采用动态规划的方法,通过维护三个变量a, b, c来统计以0开始,以1开始和以2结尾的特殊子序列的数量。每次遇到0时,可以选择包含或不包含当前0,因此特殊序列的数量是之前数量的两倍,再加上新开始的一个序列。遇到1时,可以选择包含或不包含当前1,因此以1开始的特殊序列数量是之前数量的两倍,加上所有可能以0结束的序列数量。同理,遇到2时,也是选择包含或不包含当前2,以2结束的序列数量是之前数量的两倍,加上所有可能以1结束的序列数量。最后返回以2结束的特殊子序列的数量。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在动态规划解法中,初始化a, b, c为0有什么特殊的意义,这是否意味着在序列开始之前不存在任何特殊子序列?
在给定问题中,初始化a, b, c为0确实有其特殊意义。这些变量分别表示以0开始、以1开始、以2结束的特殊子序列的数量。初始化为0意味着在序列开始之前,没有任何已形成的子序列,这是一个合理的初始化设置,因为在没有任何输入数字之前,确实不存在任何子序列。这种初始化方法为算法正确地累加新子序列提供了基础。
🦆
遇到数字0时,为什么要将a的值更新为2*a + 1,这里的+1是代表什么?
在遇到数字0时,将a的值更新为2*a + 1是因为每遇到一个新的0,我们可以选择将其包含在所有现有的以0开始的子序列中,这使得这些子序列的数量翻倍(即2*a)。此外,+1代表着开始一个全新的以该0为起点的子序列。因此,更新公式2*a + 1既包括了扩展现有子序列的数量,也包括了创建一个新的子序列。
🦆
当处理数字1和数字2时,为什么要加上之前累积的子序列数量,这样做的目的是什么?
当处理数字1时,加上之前累积的以0结束的子序列数量(即a的值),是因为每个以0结束的子序列都可以通过添加这个1来扩展成一个以1开始的新子序列。类似地,处理数字2时加上之前累积的以1结束的子序列数量(即b的值),是因为每个以1结束的子序列都可以通过添加这个2来扩展成一个以2结束的新子序列。这样的处理方式允许我们利用并扩展已存在的子序列结构,从而构建新的特殊子序列。
🦆
在更新变量b和c的公式中,为什么选择将它们与前一个状态的变量a和b相加?这样的操作有什么特别的意义吗?
在更新变量b和c的公式中,将它们与前一个状态的变量a和b相加,是为了利用之前形成的子序列以构建更长的特殊子序列。具体来说,每个以0结束的子序列(由a表示)都可以通过添加一个1来变成一个以1开始的子序列,这就是b更新时加a的原因。同样,每个以1结束的子序列(由b表示)都可以通过添加一个2来变成以2结束的子序列,这就是c更新时加b的原因。这种依赖前一个状态的累积结果的设计是动态规划方法的核心,它允许我们从前一个状态有效地构建当前状态。

相关问题