当前位置:首页 > 力扣 > LeetCode 2222题解析:高效统计"010"与"101"子序列数量的算法优化

LeetCode 2222题解析:高效统计"010"与"101"子序列数量的算法优化

2个月前 (06-21)

LeetCode 2222题解析:高效统计"010"与"101"子序列数量的算法优化  动态规划 第1张

一、题目解读

题目要求计算给定字符串中包含"010"或"101"子序列的数量。关键难点在于高效遍历所有子序列,避免重复计算。传统暴力解法时间复杂度O(n^3)会超时,需借助前缀计数与后缀计数的组合策略,将时间复杂度优化至O(n),从而满足题目要求。

二、解题思路

核心思想:利用前缀数组后缀数组记录局部统计信息,通过中间位置遍历实现O(1)查询。

    1. 预处理:创建前缀数组记录每个位置左侧"0"与"1"的数量,后缀数组记录右侧"0"与"1"的数量。

    2. 遍历中间字符:若当前为"0",则计算左侧"1"数量×右侧"1"数量(对应"101");若为"1",则计算左侧"0"数量×右侧"0"数量(对应"010")。

    3. 累加所有有效中间位置的结果,避免重复统计。

三、解题步骤

步骤1:初始化前缀与后缀数组

    创建4个数组:prefix0(前缀"0"数量)、prefix1(前缀"1"数量)、suffix0(后缀"0"数量)、suffix1(后缀"1"数量)。

    初始化首元素:根据首字符是否为"0"或"1"填充对应前缀值。

步骤2:计算前缀计数

    从左到右遍历,递推公式:prefix0[i] = prefix0[i-1] + (s[i] == '0'),prefix1[i] = prefix1[i-1] + (s[i] == '1')。

步骤3:计算后缀计数

    从右到左遍历,递推公式:suffix0[i] = suffix0[i+1] + (s[i] == '0'),suffix1[i] = suffix1[i+1] + (s[i] == '1')。

步骤4:遍历中间位置统计结果

    仅考虑i=1到n-2的位置(中间字符需两侧均有字符)。

    若s[i]=='0',则贡献为prefix1[i-1] * suffix1[i+1](左侧"1"×右侧"1")。

    若s[i]=='1',则贡献为prefix0[i-1] * suffix0[i+1](左侧"0"×右侧"0")。

步骤5:返回累加结果

    最终结果res需使用long long类型避免整数溢出。

四、代码+注释

class Solution {
public:
    long long numberOfWays(string s) {
        int n = s.size();  // 字符串长度
        vector<int> prefix0(n, 0), prefix1(n, 0);  // 前缀0/1数量
        vector<int> suffix0(n, 0), suffix1(n, 0);  // 后缀0/1数量

        // 计算前缀0和1的数量
        prefix0[0] = (s[0] == '0');  // 首字符初始化
        prefix1[0] = (s[0] == '1');
        for (int i = 1; i < n; ++i) {  // 从左到右递推
            prefix0[i] = prefix0[i-1] + (s[i] == '0');
            prefix1[i] = prefix1[i-1] + (s[i] == '1');
        }

        // 计算后缀0和1的数量
        suffix0[n-1] = (s[n-1] == '0');  // 末字符初始化
        suffix1[n-1] = (s[n-1] == '1');
        for (int i = n-2; i >= 0; --i) {  // 从右到左递推
            suffix0[i] = suffix0[i+1] + (s[i] == '0');
            suffix1[i] = suffix1[i+1] + (s[i] == '1');
        }

        long long res = 0;  // 结果需使用long long避免溢出
        for (int i = 1; i < n-1; ++i) {  // 遍历中间位置
            if (s[i] == '0') {  // 统计"101"
                res += (long long)prefix1[i-1] * suffix1[i+1];
            } else {  // 统计"010"
                res += (long long)prefix0[i-1] * suffix0[i+1];
            }
        }

        return res;
    }
};

五、总结

本文提供的算法通过前缀和后缀计数的巧妙结合,将子序列统计转化为局部信息的乘积,大幅降低时间复杂度至O(n),同时利用动态规划思想避免重复计算。代码简洁高效,适用于需要快速处理字符串子序列的场景。该解法不仅满足LeetCode 2222题的要求,也为同类问题提供了优化思路。




原创内容 转载请注明出处

标签: 动态规划
分享给朋友:

相关文章

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

一:重新解读题目泰波那契数列是一个充满数学趣味的递推序列:从第3项开始,每个数均为前三个数的和(即Tₙ₊₃ = Tₙ + Tₙ₊₁ + Tₙ₊₂)。当给定整数n时,需要高效计算出第n项的值。面对此类递...

【动态规划入门】力扣509题:斐波那契数列的经典解法与优化思路

【动态规划入门】力扣509题:斐波那契数列的经典解法与优化思路

题目解读‌斐波那契数列是一个经典的数学问题,在计算机科学中常被用作算法教学的入门案例。这个神奇的数列从0和1开始,后续每个数字都是前两个数字之和。题目要求我们计算第n个斐波那契数,看似简单的问题背后却...

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

一、题目解读牛客12576题是一道经典的算法题,要求给定起点N和终点M,求解从N到M的最少跳跃次数。题目考察的核心在于路径优化与动态规划思想,需结合数论中的质因数分解技巧,通过合理设计算法降低时间复杂...

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

一、题目解读2024年蓝桥杯省B组“传送阵”题目要求处理一个包含n个节点的图,节点间存在单向传输关系。每个节点i可传送至a[i]指定的节点,形成可能存在的环结构。题目需求解从任意节点出发能到达的最长路...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。