当前位置: 首页 > news >正文

【算法】 ‘abb‘ 型子序列问题——前后缀分解 python



‘abb’ 型子序列

题目描述

leafee 最近爱上了 abb 型语句,比如“叠词词”、“恶心心”
leafee 拿到了一个只含有小写字母的字符串,她想知道有多少个 “abb” 型的子序列?
定义: abb 型字符串满足以下条件:
字符串长度为 3 。字符串后两位相同。字符串前两位不同。

输入描述:

第一行一个正整数 𝑛
第二行一个长度为 𝑛的字符串(只包含小写字母) 1≤𝑛≤1e5

输出描述:

“abb” 型的子序列个数。

示例1

输入

6
abcbcc

输出

8

说明
共有1个abb,3个acc,4个bcc

示例2

输入

4
abbb

输出

3


第一种做法:枚举第一个 a,在后缀中寻找 bb

思路:
  1. 目标:统计满足条件的三元组 (a, b, b),其中 a 是第一个字符,b 是后两个字符,且 a != b
  2. 后缀和数组
    • 使用后缀和数组 suf[i][c] 表示从位置 i 开始到字符串末尾,字符 c 的出现次数。
    • 通过从后向前遍历字符串,填充后缀和数组。
  3. 枚举第一个字符 a
    • 对于每个位置 i,假设 s[i]a
    • 在后缀中(即从 i+1 开始)寻找字符 b,且 b 至少出现两次。
    • 如果找到满足条件的 b,则累加组合数 C(k, 2),其中 k 是字符 b 在后缀中的出现次数。
  4. 组合数计算
    • 对于字符 b 在后缀中出现 k 次,符合条件的 bb 组合数为 k * (k - 1) // 2
AC_code:
n = int(input())
s = [ord(x) - ord('a') for x in input()]
ans = 0
# 后缀和数组,suf[i][c] 表示从位置i到末尾字符c的出现次数
suf = [[0] * 26 for _ in range(n + 1)]
for i in range(n - 1, -1, -1):
    for c in range(26):
        suf[i][c] = suf[i + 1][c]
    suf[i][s[i]] += 1

for i in range(n):
    for j in range(26):
        if j != s[i] and suf[i + 1][j] >= 2:
            ans += suf[i + 1][j] * (suf[i + 1][j] - 1) // 2
print(ans)
时间复杂度:
  • 后缀和数组的构建:O(n * 26)

  • 枚举第一个字符并计算组合数:O(n * 26)

  • 总时间复杂度:O(n * 26)


第二种做法:枚举中间 b,分别在前缀中找 a,后缀中找 b

思路:
  1. 目标:统计满足条件的三元组 (a, b, b),其中 b 是中间字符,a 是左边字符,且 a != b
  2. 前缀和数组
    • 使用前缀和数组 pre[i][c] 表示前 i 个字符中字符 c 的出现次数。
    • 通过从前向后遍历字符串,填充前缀和数组。
  3. 后缀和数组
    • 使用后缀和数组 suf[i][c] 表示从位置 i 开始到末尾,字符 c 的出现次数。
    • 通过从后向前遍历字符串,填充后缀和数组。
  4. 枚举中间字符 b
    • 对于每个位置 j,假设 s[j]b
    • 在左边(即前 j 个字符)寻找字符 a,且 a != b
    • 在右边(即从 j+1 开始)寻找字符 b
    • 符合条件的组合数为左边 a 的数量乘以右边 b 的数量。
  5. 累加结果
    • 对于每个中间字符 b,累加 left * right 到答案中。
AC_code:
n = int(input())
s = [ord(x) - ord('a') for x in input()]

# 前缀和数组:pre[i][c]表示前i个字符中字符c的出现次数
pre = [[0] * 26 for _ in range(n + 1)]
for i in range(n):
    for c in range(26):
        pre[i + 1][c] = pre[i][c]
    pre[i + 1][s[i]] += 1

# 后缀和数组:suf[i][c]表示从位置i到末尾字符c的出现次数
suf = [[0] * 26 for _ in range(n + 2)]
for i in range(n - 1, -1, -1):
    for c in range(26):
        suf[i][c] = suf[i + 1][c]
    suf[i][s[i]] += 1

ans = 0
for i in range(n):
    # 左边不等于s[i]的字符数
    left = sum(pre[i][c] for c in range(26) if c != s[i])
    # 右边等于s[i]的字符数
    right = suf[i + 1][s[i]]
    ans += left * right
print(ans)
时间复杂度:
  • 前缀和数组的构建:O(n * 26)

  • 后缀和数组的构建:O(n * 26)

  • 枚举中间字符并计算结果:O(n * 26)

  • 总时间复杂度:O(n * 26)


比较:

  1. 思路差异
    • 第一种做法是枚举第一个字符 a,然后在后缀中寻找 bb
    • 第二种做法是枚举中间字符 b,然后分别在前缀中找 a,后缀中找 b
  2. 实现复杂度
    • 两种方法都需要构建前缀和和后缀和数组,实现复杂度相似。
  3. 适用场景
    • 第一种方法更适合直接统计 (a, b, b) 的组合。
    • 第二种方法更适合统计中间字符固定的组合。
  4. 性能
    • 两种方法的时间复杂度相同,均为 O(n * 26),性能相近。

总结:

  • 两种方法的核心思想都是通过预处理(前缀和和后缀和)来快速查询字符的出现次数。
  • 选择哪种方法取决于问题的具体要求和代码实现的习惯。

END
如果有更多问题或需要进一步的帮助,可以在评论区留言讨论哦!
如果喜欢的话,请给博主点个关注 谢谢

相关文章:

  • 【红队利器】单文件一键结束火绒6.0
  • 2025最新最全【大模型学习路线规划】零基础入门到精通_大模型 开发 学习路线
  • C语言 —— 此去经年 应是良辰好景虚设 - 函数
  • Deepseek 实战全攻略,领航科技应用的深度探索之旅
  • R Excel 文件:高效数据处理的利器
  • 基于SpringBoot+mybatisplus+vueJS的高校选课系统设计和实现
  • [AI]【Comfyui】 生成基本流程图的步骤保姆记录
  • 华为2025年技术发布会:智能汽车核心技术大爆发
  • Go 之 语言基本类型
  • 从0-1学习Mysql第三章: 数据类型
  • 基于Spring Boot的健康医院门诊在线挂号系统设与实现(LW+源码+讲解)
  • angular新闻列表分页
  • WPS计算机二级•文档的页面设置与打印
  • Spring参数的问题
  • P8597 [蓝桥杯 2013 省 B] 翻硬币
  • vue3中的watch
  • R语言学习笔记——确定指标权重:层次分析法/熵权法/CRITIC方法
  • 可狱可囚的爬虫系列课程 13:Requests使用代理IP
  • 分布式深度学习:探索无限可能
  • Kotlin 知识点二 延迟初始化和密封类
  • 如何做网站首页的psd图/seo专业优化公司
  • 网站开发学校/优化网站排名方法教程
  • 南开网站建设/竞价出价怎么出
  • 新网站做seo优化步骤/企业qq
  • 网页设计与制作题与答案/广州seo推广
  • wordpress七牛云token/四川seo快速排名