[Lc] 最长公共子序列 | Fenwick Tree(树状数组):处理动态前缀和

目录

LCR 095. 最长公共子序列

题解

Fenwick Tree(树状数组):处理动态前缀和

一、问题背景:当传统方法遇到瓶颈

二、Fenwick Tree核心设计

2.1 二进制索引的魔法

2.2 关键操作解析

更新操作(O(log n))

查询操作(O(log n))

三、实现细节精要

3.1 初始化优化

3.2 空间压缩技巧

四、性能对比分析

五、进阶应用场景

六、实现注意事项

七、与其他数据结构的配合

延伸阅读


LCR 095. 最长公共子序列

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

题解

两个字符串可能存在多个公共子序列,题目要求计算最长公共子序列的长度,因此可以考虑使用动态规划来解决。

二维DP数组

  • 用函数 f(i, j) 表示字符串 s1 中下标从 0 开始到 i 的子字符串 s1[0...i] 和字符串 s2 中下标从 0 开始到 j 的子字符串 s2[0...j] 的最长公共子序列的长度。
  • 对于 f(i, j),如果 s1[i] == s2[j],那么相当于在 s1[0...i - 1] 和 s2[0...j - 1] 的最长公共子序列的后面添加一个公共字符,也就是 f(i, j) = f(i - 1, j - 1) + 1。
  • 如果 s1[i] != s2[j],那么这两个字符不可能出现在 s1[0...i] 和 s2[0...j] 的公共子序列中。
  • 此时 s1[0...i] 和 s2[0...j] 的最长公共子序列是s1[0...i - 1] 和 s2[0...j] 的最长公共子序列和s1[0...i] 和 s2[0...j - 1] 的最长公共子序列中的较大值,即 f(i, j) = max(f(i - 1, j), f(i, j - 1))。 (子序列的特性,决定了可以这么写
  • 所以转移状态方程为

因为状态方程有两个变量,所以需要使用二维矩阵保存。同时上述方程会出现 i 或者 j 出现 -1 的情况,代表出现 -1 下标的字符串的子串目前是空的,那么就不会有公共子序列,所以 f(i, -1) = f(-1, j) = 0。

以 "abcde" 和 "badfe" 为例子,二维状态矩阵如下图

一开始先完成 f(i, -1) = f(-1, j) = 0 初始化,之后二维矩阵按照从左往右逐行向下遍历填充。

  • 推荐使用逐行而不是逐列,虽然不影响算法,但是考虑到二维数组本身是按照一维数组存储以及计算机缓存的运行机制,按照逐行遍历的方式效率更高点。
  • 完整的代码如下,若 s1 和 s2 的长度分别为 m 和 n,那么时间复杂度为 O(mn),空间复杂度为 O(mn)。
class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int n1 = text1.size();int n2 = text2.size();vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1));for (int i = 0; i < n1; ++i) {for (int j = 0; j < n2; ++j) {if (text1[i] == text2[j]) {dp[i + 1][j + 1] = dp[i][j] + 1;} else {dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);}}} return dp[n1][n2];}
};
  • 当前==,就f(i - 1, j - 1) + 1
  • 当前不等,就存遍历过的最大max(f(i - 1, j), f(i, j - 1))

遍历

将 i==0 和 i==3 的情况来查看一下

2179. 统计数组中好三元组数目

给你两个下标从 0 开始且长度为 n 的整数数组 nums1nums2 ,两者都是 [0, 1, ..., n - 1]排列

好三元组 指的是 3互不相同 的值,且它们在数组 nums1nums2 中出现顺序保持一致。换句话说,如果我们将 pos1v 记为值 vnums1 中出现的位置,pos2v 为值 vnums2 中的位置,那么一个好三元组定义为 0 <= x, y, z <= n - 1 ,且 pos1x < pos1y < pos1zpos2x < pos2y < pos2z 都成立的 (x, y, z)

请你返回好三元组的 总数目

示例 1:

输入:nums1 = [2,0,1,3], nums2 = [0,1,2,3]
输出:1
解释:
总共有 4 个三元组 (x,y,z) 满足 pos1x < pos1y < pos1z ,分别是 (2,0,1) ,(2,0,3) ,(2,1,3) 和 (0,1,3) 。
这些三元组中,只有 (0,1,3) 满足 pos2x < pos2y < pos2z 。所以只有 1 个好三元组。

示例 2:

输入:nums1 = [4,0,1,3,2], nums2 = [4,1,0,2,3]
输出:4
解释:总共有 4 个好三元组 (4,0,3) ,(4,0,2) ,(4,1,3) 和 (4,1,2) 。

下标比较相当于是一种映射规则。现在我在这种映射规则上再施加一种映射规则,让在nums1中满足下标递增这种性质映射为键值递增这种性质。

同样的对nums2施加这种规则,下标递增也应该映射为键值递增。有一点点往这方面想,不看题解根本无法完全想出来。

template<typename T>
class FenwickTree {vector<T> tree;public:// 使用下标 1 到 nFenwickTree(int n) : tree(n + 1) {}// a[i] 增加 val// 1 <= i <= nvoid update(int i, T val) {for (; i < tree.size(); i += i & -i) {tree[i] += val;}}// 求前缀和 a[1] + ... + a[i]// 1 <= i <= nT pre(int i) const {T res = 0;for (; i > 0; i &= i - 1) {res += tree[i];}return res;}
};class Solution {
public:long long goodTriplets(vector<int>& nums1, vector<int>& nums2) {int n = nums1.size();vector<int> p(n);for (int i = 0; i < n; i++) {p[nums1[i]] = i;}long long ans = 0;FenwickTree<int> t(n);for (int i = 0; i < n - 1; i++) {int y = p[nums2[i]];int less = t.pre(y);ans += (long) less * (n - 1 - y - (i - less));t.update(y + 1, 1);}return ans;}
};

Fenwick Tree(树状数组):处理动态前缀和

一、问题背景:当传统方法遇到瓶颈

在数据处理场景中,我们常需要快速计算数组的前缀和(Prefix Sum)。

传统的前缀和数组方法虽然能在O(1)时间完成查询,但面对频繁更新的动态数据时,其O(n)的更新时间复杂度将成为性能瓶颈。

想象一个股票交易系统需要实时追踪每分钟的成交额统计,这种场景正是Fenwick Tree大显身手的战场。

二、Fenwick Tree核心设计

2.1 二进制索引的魔法

该数据结构的核心创新在于将数组索引的二进制表示转化为层次结构。每个索引对应的二进制最低有效位(LSB)决定了其管理的数据范围:

  • 索引i的LSB位置k(从1开始计数)
  • 该节点负责管理区间[i - 2^{k-1} + 1, i]的数据

2.2 关键操作解析

更新操作(O(log n))
def update(i, delta):while i <= n:tree[i] += deltai += i & -i  # 找到下一个父节点
查询操作(O(log n))
def query(i):res = 0while i > 0:res += tree[i]i -= i & -i  # 剥离最低有效位return res

三、实现细节精要

3.1 初始化优化

# 最优初始化方式(时间复杂度O(n))
def build(arr):n = len(arr)tree = [0]*(n+1)for i in range(1, n+1):tree[i] += arr[i-1]j = i + (i & -i)if j <= n:tree[j] += tree[i]return tree

3.2 空间压缩技巧

通过将原始数组映射到树状数组的奇数索引位置,可以节省约50%的内存空间。这种优化在嵌入式系统或处理海量数据时尤为重要。

四、性能对比分析

操作类型

前缀和数组

Fenwick Tree

初始化

O(n)

O(n)

单点更新

O(n)

O(log n)

范围查询

O(1)

O(log n)

空间复杂度

O(n)

O(n)

适用场景选择指南

  • 静态数据:优先选择前缀和数组
  • 动态数据:必选Fenwick Tree
  • 混合操作:根据更新/查询频率比例决策

五、进阶应用场景

  1. 逆序对统计:结合离散化技术,可在O(n log n)时间内完成
  2. 多维扩展:二维Fenwick Tree支持矩阵区域求和
  3. 频率统计:实现高效的可变范围频率查询
  4. 实时推荐系统:动态维护用户行为热度排名

六、实现注意事项

  1. 索引通常从1开始(避免处理0的特殊情况)
  2. 更新操作是增量式的(delta为变化量而非绝对值)
  3. 合理选择数据存储类型(防止整数溢出)
  4. 注意缓存友好性(顺序访问优于随机访问)

七、与其他数据结构的配合

  • 与线段树结合实现区间更新/区间查询
  • 联合哈希表处理稀疏数据
  • 配合持久化技术实现历史版本查询

延伸阅读

  • 原理解析视频
  • 《算法导论》第15章高级数据结构
  • ACM竞赛中的经典应用案例集

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/77039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python3.13.0环境安装及python-docx库安装指南

1. Python环境安装 1.1 Windows系统安装Python 下载Python安装包 • 访问Python官网 • 点击"Download Python 3.x.x"&#xff08;推荐使用3.8及以上版本&#xff09; 2. 运行安装程序 • 双击下载的安装包 • 重要&#xff1a;勾选"Add Python to environmen…

前端VUE框架理论与应用(4)

一、计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: <div id="example">{{ message.split().reverse().join() }}</div> 在这个地方,模板不再是简单的声明式逻辑。你…

MySQL:存储函数和存储过程

系列文章目录 1.MySQL编程基础 2.程序控制流语句 3.存储过程 4.游标 5.嵌入式SQL 文章目录 系列文章目录前言一、程序控制流语句&#xff1a;二、存储函数&#xff1a; 1.存储函数的特点&#xff1a;2.存储函数的定义&#xff1a;3.调用存储函数 三、存储过程&#xff1a;…

基础贪心算法集合2(10题)

目录 1.单调递增的数字 2.坏了的计算器 3.合并区间 4.无重叠区间 5. 用最少数量的箭引爆气球 6.整数替换 解法1&#xff1a;模拟记忆化搜索 解法2位运算贪心 7.俄罗斯套娃信封问题 补充.堆箱子 8.可被3整除的最大和 9.距离相等的条形码 10.重构字符串 1.单调递增的数字…

RaabitMQ 快速入门

&#x1f389;欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ &#x1f389;感谢各位读者在百忙之中抽出时间来垂阅我的文章&#xff0c;我会尽我所能向的大家分享我的知识和经验&#x1f4d6; &#x1f389;希望我们在一篇篇的文章中能够共同进步&#xff01;&#xff01;&…

语音识别——根据声波能量、VAD 和 频谱分析实时输出文字

SenseVoiceSmall网络结构图 ASR(语音识别)是将音频信息转化为文字的技术。在实时语音识别中,一个关键问题是:如何决定将采集的音频数据输入大模型的最佳时机?固定时间间隔显然不够灵活,太短可能导致频繁调用模型,太长则会延迟文字输出。有没有更智能的方式?答案是肯定…

AI大模型如何重塑科研范式:从“假说驱动”到“数据涌现”

📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:科研进入“模型共研”时代 传统科研范式通常以“假设→实验→验证→理论”的方式推进,这一经典路径建立在人类的认知能力与逻辑推理基础上。然而,随着数据规模的爆炸式增长与知识系统的高度复杂…

使用Python写入JSON、XML和YAML数据到Excel文件

在当今数据驱动的技术生态中&#xff0c;JSON、XML和YAML作为主流结构化数据格式&#xff0c;因其层次化表达能力和跨平台兼容性&#xff0c;已成为系统间数据交换的通用载体。然而&#xff0c;当需要将这类半结构化数据转化为具备直观可视化、动态计算和协作共享特性的载体时&…

面试题:Eureka和Nocas的区别

Eureka 与 Nacos 核心区别对比 一、功能定位与核心能力 ‌维度‌‌Eureka‌‌Nacos‌‌核心功能‌专注服务注册与发现&#xff0c;无配置管理功能‌:ml-citation{ref“1,3” data“citationList”}集成服务注册、发现、配置管理、动态DNS等‌:ml-citation{ref“1,3” data“c…

2025年4月15日 百度一面 面经

目录 1. 代理相关 从静态代理到动态代理 2. cglib可以代理被final修饰的类吗,为什么 3. JVM 体系结构 4. 垃圾回收算法 5. 什么是注解 如何使用 底层原理 6. synchronized和reentrantlock 7. 讲一下你项目中 redis的分布式锁 与java自带的锁有啥区别 8. post 请求和 ge…

AI改变生活

AI改变生活 人工智能&#xff08;AI&#xff09;在我们生活中的应用越来越广泛&#xff0c;深刻地改变了我们的工作和生活方式。以下是一些AI实际应用的实例&#xff0c;以及它们如何影响我们的日常生活。 1. 智能助手 智能助手如Siri、Alexa和Google Assistant等&#xff0…

信奥赛之c++基础(取模运算与数位分离)

🎮 数字拆解大冒险——取模运算与数位分离魔法课 🍬 第一章:糖果分装术——取模运算 🍭 分糖果游戏 7颗糖每人分3颗: 每人得到:7 / 3 = 2颗剩余糖果:7 % 3 = 1颗(%就是取模符号) 就像把糖果装袋后剩下的零散糖粒!🔧 取模运算说明书 算式比喻结果10 % 310颗糖分…

揭秘大数据 | 21、软件定义计算

老夫先将这个小系列的前两篇内容链接奉上&#xff0c;方便感兴趣的朋友一气读之。 揭秘大数据 | 19、软件定义的世界-CSDN博客 揭秘大数据 | 20、软件定义数据中心-CSDN博客 今天&#xff0c;书接上文&#xff0c;开聊软件定义计算的那些事儿&#xff01; 虚拟化是软件定义…

FPGA-DDS技术的波形发生器

1.实验目的 1.1掌握直接数字频率合成&#xff08;DDS&#xff09;的基本原理及其实现方法。 1.2在DE2-115 FPGA开发板上设计一个可调频率的正弦波和方波发生器&#xff0c;频率范围10Hz~5MHz&#xff0c;最小分辨率小于1kHz。 1.3使用Quartus II进行仿真&#xff0c;并通过S…

LeetCode[541]反转字符串Ⅱ

思路&#xff1a; 题目给我们加了几个规则&#xff0c;剩余长度小于2k&#xff0c;大于等于k就反转k个&#xff0c;小于k就全部反转&#xff0c;我们按照这个逻辑来就行。 第一就是大于等于k就反转k个&#xff0c;我们for循环肯定是i2k了&#xff0c;接下来就是判断是否大于等于…

实现定长的内存池

池化技术 所谓的池化技术&#xff0c;就是程序预先向系统申请过量的资源&#xff0c;然后自己管理起来&#xff0c;以备不时之需。这个操作的价值就是&#xff0c;如果申请与释放资源的开销较大&#xff0c;提前申请资源并在使用后并不释放而是重复利用&#xff0c;能够提高程序…

路由器原理与配置技术详解

一、路由基础原理 1.1 路由器的核心功能 网络层设备&#xff1a;工作在OSI参考模型第三层&#xff0c;实现不同网络间的互联互通智能路径选择&#xff1a;基于路由表为数据包选择最优传输路径协议转换&#xff1a;处理不同网络接口间的协议差异&#xff08;如以太网与PPP&…

Leetcode 3518. Smallest Palindromic Rearrangement II

Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解题思路2. 代码实现 题目链接&#xff1a;Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解题思路 这一题是题目Leetcode 3517. Smallest Palindromic Rearrangement I的升级版本&#xff0c;其主要的…

大模型——Crawl4AI 中的数据提取策略

大模型——Crawl4AI 中的数据提取策略 在本章中,将详细介绍在 Crawl4AI 中可用的数据提取策略。这些策略包括: LLMExtractionStrategy:用于详细内容提取。JsonCssExtractionStrategy:使用 CSS 选择器进行结构化数据检索。CosineStrategy:基于余弦相似性进行有效的语义分段…

职坐标解码互联网行业转型发展新动能

当前&#xff0c;互联网行业正以前所未有的速度重塑全球产业格局。工信部最新数据显示&#xff0c;我国互联网企业营收连续三年保持双位数增长&#xff0c;其中百强企业在人工智能、物联网等领域的投入强度同比提升40%&#xff0c;展现出强劲的技术引领力。与此同时&#xff0c…