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 所有元音字符
vPre1i 前i个字符中元音的数量
vPre2i 前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 = vPre1j + 1 - vPre1i;

const int iNum2 = vPre2j + 1 - vPre2i;

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(v1i, v2i);

}

}

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);

}

}

方案二

sleft,right是美丽字符的条件。

一,元音辅音相等。我们记录所有subleft = vPre1left-vPre2left,即元音辅音之差。如果subleft等于subright,则元音辅音相等。

二,数量的平方是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)。

代码

cpp 复制代码
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。

代码

cpp 复制代码
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

相关推荐
ai产品老杨11 分钟前
H264 H265视频分析常见问题和排查清单
人工智能·算法·音视频
Jerry37 分钟前
LeetCode 454. 四数相加 II
算法
可编程芯片开发1 小时前
基于CPS-SPWM链式STATCOM系统在电压不平衡环境下控制策略的simulink建模与仿真
算法
Jerry2 小时前
LeetCode 202. 快乐数
算法
hans汉斯2 小时前
基于改进交叉熵损失函数与Transformer的心电信号高风险分类研究
功能测试·深度学习·算法·yolo·目标检测·分类·transformer
Jerry2 小时前
LeetCode 349. 两个数组的交集
算法
熊猫_豆豆3 小时前
QT6 Android C++ 自制美观闹钟
android·c++·qt·闹钟
YuK.W3 小时前
Leetcode100: 70.爬楼梯、118.杨辉三角、198.打家劫舍
java·算法·leetcode
随意起个昵称3 小时前
状压dp-基础题目2([USACO12MAR] Cows in a Skyscraper G)
c++·算法·动态规划
Jerry4 小时前
LeetCode 1002. 查找共用字符
算法