蓝桥杯子串分值(时间复杂度优化)

题目

传送门
f(“aba”)=1 因为b恰好出现一次
在这里插入图片描述
在这里插入图片描述

解析

一般思维:遍历所有子串,找只出现1次的字母的个数
正确思维:遍历字符串的每个字符,找出该字符贡献的子串数目
如f(“abcc”)=2 是 a对这个"abcc"了一次 ,b对"abcc"贡献了一次
参考博客:传送门
在这里插入图片描述
这里依然以abaca为例,
当遍历到i=1(字符a时),该字符贡献的子串数为:左侧只能不选,右侧可以不选或是选1个,所以种类数为 1*2=2;
当遍历到i=2(字符b时),该字符贡献的子串数为:左侧可以不选或是选1个,右侧可以不选或是选1,2,3个,所以种类数为2*4=8;
当遍历到i=3(字符a时),该字符贡献的子串数为:左侧可以不选或是选1个,右侧可以不选或是选1个,所以种类数为2*2=4;
以此类推。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll> vi[30];//存放每个字母出现的位置 
string s;
void begin_init() 
{
	for(int i=0;i<26;i++)
	{
		vi[i].push_back(0);	
	}
}
void end_init()
{
	for(int i=0;i<26;i++)
	{
		vi[i].push_back(s.size()+1);	
	}
}
int main()
{
	ll ans=0;
	cin>>s;
	begin_init();
	for(ll i=0;i<s.size();i++)
	{
		vi[s[i]-'a'].push_back(i+1);
	}
	end_init();
	//处理完后直接遍历vi数组即可
	for(int i=0;i<26;i++) 
	{
		if(vi[i].size()>2)
		{
			for(int j=1;vi[i][j]!=s.size()+1;j++)
			{
				ll temp1=vi[i][j]-vi[i][j-1];
				ll temp2=vi[i][j+1]-vi[i][j];
				ans+=temp1*temp2;
			}
		}
	}
	cout<<ans;
}
### 蓝桥杯字符简写与前缀和的算法解题思路 #### 字符简写的解题思路 在蓝桥杯竞赛中,涉及字符简写的题目通常会考察如何简化字符表示方式。例如,给定一个字符 `S` 和两个字符 `c1` 和 `c2`,要求统计以 `c1` 开头、以 `c2` 结尾且长度大于等于 `K` 的子串数量[^3]。 为了实现这一目标,可以通过双重循环枚举所有可能的子串起点和终点位置,并判断其是否符合条件。具体来说: - 枚举字符中的每一个字符作为潜在的结尾字符 `c2`。 - 计算对应的起始索引位置是否能够找到开头字符 `c1`,即验证是否存在关系 \( \text{start\_index} = i - K + 1 \geq 0 \)[^5]。 - 如果上述条件成立,则计数器加一。 这种方法虽然简单直观,但在处理大规模数据时可能会面临性能瓶颈。因此,可以考虑引入更高效的预处理技术来加速查询过程。 #### 前缀和的应用场景 前缀和是一种用于快速求解数组部分和的技术,在解决某些特定类型的编程问题时非常有用。它不仅可以应用于数值型数组上的一维情况,还可以扩展到矩阵上的二维情形[^1]。 在一维情况下,构建前缀和数组可以帮助我们在常数时间内获取任意区间的总和值。这使得当我们面对频繁询问某范围内的累积量需求时变得极为便利。比如在一个序列里多次请求不同片段之和的时候,先计算好整个列表从前至后的累加结果存储起来形成所谓的“前缀和”,之后再利用这个预先准备好的信息就能迅速得出答案。 对于二维平面而言,同样也可以建立类似的机制——只是现在我们需要维护的是矩形区域内部元素合计的信息而非单纯线性排列的数据项而已。这样做的好处在于一旦完成了初始阶段繁琐但仅需执行一次性的准备工作之后,后续针对任何指定大小的小方格内含有的项目总数都能够瞬间获得响应。 结合前面提到有关字符操作的任务背景来看的话,假设我们的输入不仅仅是一般的字母组合而成的文字链表那么简单明了;而是附加了一些额外属性或者权重参数附着在其各个组成部分之上(譬如说每个单词都有各自独立的重要性评分之类的设定),那么此时运用此类技巧将会显得尤为重要:因为这样一来便允许程序员们能够在几乎不增加额外时间开销的前提下完成原本较为复杂的运算逻辑转换工作流程自动化程度极大提升的同时也保证了最终产出成果的质量稳定性得到了有效保障。 ```python def count_substrings(s, c1, c2, k): n = len(s) result = 0 prefix_sum_c1 = [0] * (n + 1) for i in range(1, n + 1): prefix_sum_c1[i] = prefix_sum_c1[i - 1] + (s[i - 1] == c1) for end_index in range(k - 1, n): start_index = end_index - k + 1 if s[end_index] != c2: continue num_of_c1_in_range = prefix_sum_c1[start_index + 1] - prefix_sum_c1[start_index] if num_of_c1_in_range >= 1: result += 1 return result ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值