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

简介: 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 做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



相关文章
|
1月前
|
JSON 算法 数据挖掘
基于图论算法有向图PageRank与无向图Louvain算法构建指令的方式方法 用于支撑qwen agent中的统计相关组件
利用图序列进行数据解读,主要包括节点序列分析、边序列分析以及结合节点和边序列的综合分析。节点序列分析涉及节点度分析(如入度、出度、度中心性)、节点属性分析(如品牌、价格等属性的分布与聚类)、节点标签分析(如不同标签的分布及标签间的关联)。边序列分析则关注边的权重分析(如关联强度)、边的类型分析(如管理、协作等关系)及路径分析(如最短路径计算)。结合节点和边序列的分析,如子图挖掘和图的动态分析,可以帮助深入理解图的结构和功能。例如,通过子图挖掘可以发现具有特定结构的子图,而图的动态分析则能揭示图随时间的变化趋势。这些分析方法结合使用,能够从多个角度全面解读图谱数据,为决策提供有力支持。
|
2月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
687 0
高精度算法(加、减、乘、除,使用c++实现)
|
2月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
49 0
|
2月前
|
缓存 网络协议 API
C/C++ StringToAddress(字符串转 boost::asio::ip::address)
通过上述步骤和示例代码,你可以轻松地在C++项目中实现从字符串到 `boost::asio::ip::address`的转换,从而充分利用Boost.Asio库进行网络编程。
89 0
|
2月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
|
2月前
|
存储 算法 C++
【算法】哈希映射(C/C++)
【算法】哈希映射(C/C++)
|
2月前
|
机器学习/深度学习 人工智能 算法
【算法】最长公共子序列(C/C++)
【算法】最长公共子序列(C/C++)
|
2月前
|
人工智能 算法 BI
一篇带你速通差分算法(C/C++)
一篇带你速通差分算法(C/C++)
|
2月前
|
人工智能 算法 C++
一篇带你速通前缀和算法(C/C++)
一篇带你速通前缀和算法(C/C++)
|
2月前
|
存储 算法 C++
弗洛伊德(Floyd)算法(C/C++)
弗洛伊德(Floyd)算法(C/C++)