C++前缀和算法:统计美丽子字符串

题目

给你一个字符串 s 和一个正整数 k 。
用 vowels 和 consonants 分别表示字符串中元音字母和辅音字母的数量。
如果某个字符串满足以下条件,则称其为 美丽字符串 :
vowels == consonants,即元音字母和辅音字母的数量相等。
(vowels * consonants) % k == 0,即元音字母和辅音字母的数量的乘积能被 k 整除。
返回字符串 s 中 非空美丽子字符串 的数量。
子字符串是字符串中的一个连续字符序列。
英语中的 元音字母 为 ‘a’、‘e’、‘i’、‘o’ 和 ‘u’ 。
英语中的 辅音字母 为除了元音字母之外的所有字母。
示例 1:
输入:s = “baeyh”, k = 2
输出:2
解释:字符串 s 中有 2 个美丽子字符串。

  • 子字符串 “baeyh”,vowels = 2([“a”,“e”]),consonants = 2([“y”,“h”])。
    可以看出字符串 “aeyh” 是美丽字符串,因为 vowels == consonants 且 vowels * consonants % k == 0 。
  • 子字符串 “baeyh”,vowels = 2([“a”,“e”]),consonants = 2([“b”,“y”])。
    可以看出字符串 “baey” 是美丽字符串,因为 vowels == consonants 且 vowels * consonants % k == 0 。
    可以证明字符串 s 中只有 2 个美丽子字符串。
    示例 2:
    输入:s = “abba”, k = 1
    输出:3
    解释:字符串 s 中有 3 个美丽子字符串。
  • 子字符串 “abba”,vowels = 1([“a”]),consonants = 1([“b”])。
  • 子字符串 “abba”,vowels = 1([“a”]),consonants = 1([“b”])。
  • 子字符串 “abba”,vowels = 2([“a”,“a”]),consonants = 2([“b”,“b”])。
    可以证明字符串 s 中只有 3 个美丽子字符串。
    示例 3:
    输入:s = “bcdf”, k = 1
    输出:0
    解释:字符串 s 中没有美丽子字符串。
    参数范围
    1 <= s.length <= 5 * 104
    1 <= k <= 1000
    s 仅由小写英文字母组成。

方法一

分析

时间复杂度

O(n)

大致步骤

记录前缀和后,枚举左右端点。

setVowel所有元音字符
vPre1[i]前i个字符中元音的数量
vPre2[i]前i个字符中辅音的数量

代码

核心代码

class Solution {
public:
int beautifulSubstrings(string s, int k) {
m_c = s.length();
std::unordered_set setVowel = { ‘a’,‘e’,‘i’,‘o’ , ‘u’ };
vector vPre1 = { 0 }, vPre2 = { 0 };
for (const char& ch : s)
{
if (setVowel.count(ch))
{
vPre1.emplace_back(vPre1.back() + 1);
vPre2.emplace_back(vPre2.back() );
}
else
{
vPre1.emplace_back(vPre1.back() );
vPre2.emplace_back(vPre2.back() + 1);
}
}
int iRet = 0;
for(int i = 0 ; i < m_c ; i++ )
for (int j = i; j < m_c; j++)
{
const int iNum1 = vPre1[j + 1] - vPre1[i];
const int iNum2 = vPre2[j + 1] - vPre2[i];
if (iNum1 != iNum2)
{
continue;
}
if (0 != iNum1 * iNum2% k )
{
continue;
}
iRet++;
}
return iRet;
}
int m_c;
};

测试用例

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}
int main()
{
string s;
int k,res;
{
Solution slu;
s = “baeyh”;
k = 2;
res = slu.beautifulSubstrings(s, k);
Assert(res, 2);
}
{
Solution slu;
s = “abba”;
k = 1;
res = slu.beautifulSubstrings(s, k);
Assert(res, 3);
}
{
Solution slu;
s = “bcdf”;
k = 1;
res = slu.beautifulSubstrings(s, k);
Assert(res, 0);
}
{
Solution slu;
s = “ihroyeeb”;
k = 5;
res = slu.beautifulSubstrings(s, k);
Assert(res, 0);
}
}

方案二

s[left,right]是美丽字符的条件。
一,元音辅音相等。我们记录所有sub[left] = vPre1[left]-vPre2[left],即元音辅音之差。如果sub[left]等于sub[right],则元音辅音相等。
二,数量的平方是k的倍数。我可以转成等效问题:数量必须是m的倍数。如:k=4,则m=2。k=3,则m=3。k=12,m=6。显然:m小于等于k,且m不会为0。对于每个left,我们无需记录它的元音数量,只需要记录它的元音数量%m。

时间复杂度

如果用有序映射记录状态的数量,则时间复杂度为:O(nlognm)。
枚举每个每个美丽字符串的右端点时间复杂度O(n),查询合法的对应left数量O(lognm)。如果用哈希映射记录状态和数量,总时间复杂度降到O(n)。

代码

class Solution {
public:int beautifulSubstrings(string s, int k) {m_c = s.length();std::unordered_set<char> setVowel = { 'a','e','i','o' , 'u' };vector<int> vPre1 = { 0 }, vPre2 = { 0 };for (const char& ch : s){if (setVowel.count(ch)){vPre1.emplace_back(vPre1.back() + 1);vPre2.emplace_back(vPre2.back());}else{vPre1.emplace_back(vPre1.back());vPre2.emplace_back(vPre2.back() + 1);}}int m = 0;for (m = 1; 0 != m * m % k; m++);int iRet = 0;std::unordered_map<int, std::unordered_map<int,int>> mSub;for (int i = 0; i < m_c; i++){const int iSub = vPre1[i+1] - vPre2[i+1];const int iNeed = vPre1[i + 1] % m;if (mSub.count(iSub)){if(mSub[iSub].count(iNeed)){iRet += mSub[iSub][iNeed];}}{const int iSub = vPre1[i] - vPre2[i];mSub[iSub][vPre1[i]%m]++;}}return iRet;}int m_c;
};

优化代码

分析

优化点:
一,无需前缀和,记录当前元音数量就可以了。当前辅音数量=当前字符总数量-当前元音数量。
二,用std::pair<int,int> 做key。

代码

class Solution {
public:int beautifulSubstrings(string s, int k) {m_c = s.length();std::unordered_set<char> setVowel = { 'a','e','i','o' , 'u' };int m = 0;for (m = 1; 0 != m * m % k; m++);int iRet = 0;int iVowelNum = 0;std::map<std::pair<int, int>, int> mSubVowelToNum;for (int i = 0; i < m_c; i++){const int preVowel = iVowelNum;if (setVowel.count(s[i])){iVowelNum++;}const int iSub = iVowelNum - (i+1- iVowelNum);//当前元音数量减辅音数量auto pr = std::make_pair(iSub, iVowelNum%m);if (mSubVowelToNum.count(pr)){iRet += mSubVowelToNum[pr];}{const int iSub = preVowel - (i  - preVowel);auto pr = std::make_pair(iSub, preVowel%m);mSubVowelToNum[pr]++;}}return iRet;}int m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境:

VS2022 C++17

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

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

相关文章

P18 C++ 继承

目录 前言 01 不使用继承会让你多打很多无用的代码 02 继承 最后的话 前言 本期我们学习 C 面向对象编程中的继承。 面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类&#xff0c;这使得创建和维护一个应用程序变得更容易。这样做&#…

操作系统——解决了我的一些困惑

目录 1、电脑开机做了什么事情 2、真正实现并行的计算机 3、计算机中的淘汰算法 & 分配算法 & 调度算法 & 空间管理 4、什么是虚拟内存&#xff1f;为什么需要虚拟内存&#xff1f;最多可分配多少&#xff1f; 5、TLB&#xff08;快表&#xff09;、分页存储&…

详解RT-DETR网络结构/数据集获取/环境搭建/训练/推理/验证/导出/部署

论文地址&#xff1a;RT-DETR论文地址 代码地址&#xff1a;RT-DETR官方下载地址 目录 一、本文介绍 二、RT-DETR的网络结构 2.1、模型概览 2.2、高效混合编码器 2.3、IoU感知查询选择 2.4、 可扩展的RT-DETR 三、RT-DERT的环境搭建 四、免费数据集获取 五、获取RT-D…

【PyQt】(自定义类)阴影遮罩-升级版

这是之前发的代码(自定义类)阴影遮罩的升级版。 升级就升级在&#xff0c;优化了对非矩形控件的遮盖效果&#xff0c;例如圆角按钮&#xff0c;以及默认方法不满足时可以传入其他的遮盖方法。 自定义阴影遮罩Mask&#xff1a; class Mask(QWidget):__excludeNone__colorNonecl…

基于51单片机的信号发生器仿真设计

这里写目录标题 基于51单片机信号发生器仿真设计1 设计说明&#xff1a;2 讲解视频&#xff1a;3 仿真电路&#xff1a;程序LCD1602显示按键扫描主函数正弦波发生子程序矩形波发生子程序三角波发生子程序锯齿波发生子程序 设计说明书资料清单&&下载链接资料下载链接 基…

Android aidl的简单使用

一.服务端 1.创建aidl文件&#xff0c;然后记得build下生成java文件 package com.example.aidlservice31;// Declare any non-default types here with import statementsinterface IMyAidlServer {// 接收一个字符串参数void setData(String value);// 返回一个字符串String …

树状数组专题

折叠 区间修改&#xff0c;区间查询&#xff0c;这一类题通常都可以使用线段树解决&#xff0c;但对于此题&#xff0c;树状数组同样可以&#xff0c;而且常数较小&#xff0c;代码简单。 思路&#xff1a; 考虑使用树状数组去维护差分数组&#xff0c;即对于 a i a_i ai​,我们…

国内20个大模型中文场景测评及体验

中文场景能力测评 SuperCLUE排行榜 大模型及网站 公司&#xff08;大模型&#xff09; 智能程度 借鉴点 体验网站 备注 1 百度文心一言 高   文心一言   2 百川智能 高   百川大模型-汇聚世界知识 创作妙笔生花-百川智能   3 商汤商量SenseChat&#xff…

Rust语言入门教程(八) - 引用与借用

上一章的内容中我们讨论了Rust的所有权系统&#xff0c;当我们不想移动值的所有权时&#xff0c;我们可以使用引用和借用&#xff0c;而这正是本章想要讨论的问题。 引用&#xff08;References&#xff09; 引用允许你访问或修改数据而无需获取数据的所有权。在 Rust 中&…

论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools

论文笔记--Toolformer: Language Models Can Teach Themselves to Use Tools 1. 文章简介2. 文章概括3 文章重点技术3.1 Toolformer3.2 APIs 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;Toolformer: Language Models Can Teach Themselves to Use Tools作者&#…

鸿蒙(HarmonyOS)应用开发——装饰器

简介 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript&#xff08;简称TS&#xff09;的基础上&#xff0c;扩展了声明式UI、状态管理等相应的能力&#xff0c;让开发者可以以更简洁、更自然的方式开发高性能应用。TS是JavaScript&#xff08;简称JS&#xff09;的超…

【浅尝C++】类与对象之定义、实例化、访问限定符及this指针

&#x1f388;归属专栏&#xff1a;浅尝C &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;记录一句&#xff1a;好想摆烂&#xff0c;又好想学习~~ 文章前言&#xff1a;本篇文章简要介绍C类的定义、实例化、访问限定符和this指针&#xff0c;介绍每个小点时&#…