蓝桥杯 历届真题 子串分值和【第十一届】【省赛】【A组】C++ 简单计数原理

资源限制

时间限制:1.0s 内存限制:256.0MB


分析:

乘法原理 O ( N ) :

解题思路:

统计每个字母在仅出现一次的情况下,能被多少子串所包含;
pre[i] 记录第 i 个字母上一次出现的位置,用 next[i] 记录第 i 个字母下一次出现的位置;
那么
往左最多能延伸到 pre[i] + 1,其到第 i 个字母一共有 i - pre[i] 个字母
同理
往右最多能延伸到 next[i] - 1,其到第 i 个字母一共有 next[i] - i 个字母
二者相乘,就是该字母被不同子串所包含的总次数;

再换种说法分析,即对于字符串中的每个字符,我们可以考虑它对答案的贡献度,即 它 所能在的所有的字符子串中,它是唯一的个数,设某个字符位置位a,和它相同的前一个字符位置为b,和它相同的后一个字符位置为c,那么他的贡献度为(a-b)*(c-a),其中a-b种取法是在a和b之间可以取0到a-b-1个字符。同理,a和c之间可以取0到c-a-1个字符,即c-a种方法。两者相乘即是a作为某字串中唯一的字符时的贡献。运用了简单的计数原理。
为了加快时间,我们预处理每个字符的上一个位置和下一个位置。

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5, M = 1e5 + 5;
int pre[N], ne[N], ans;//pre[i]的值即 对于下标为i的字符的前一个相同字符的位置
int last[30];//记录某字母最后一次出现的位置 
int main() {
	string s;
	getline(cin, s);
	int len = s.length();
	memset(last, -1, sizeof(last));//前面没有就是-1,这样0-(-1)是1 
	for (int i = 0; i < len; i++) {
		pre[i] = last[s[i] - 'a'];//每个字符-'a'相当于得到它和'a'的距离,可以作为独特下标索引
		//因为循环会不断的更新pre和last数组,所以pre[i]最终存的就是
		//前面一个和自己相同的字符的下标,即是上一次更新后的最后的此字符位置
		last[s[i] - 'a'] = i; //更新s[i]字符最后一次出现的位置
	}
	for (int i = 0; i < 26; i++)	last[i] = len;//如果后面没有就是len。因为最多只能从最后一位选
	//为什么要倒着推,因为我们要知道x字符的下一个位置,必然需要提前知道他后面的 
	for (int i = len - 1; i >= 0; i--) {
		ne[i] = last[s[i] - 'a'];//与pre的更新同理,倒着更新使得ne[i]存着对于i下标字符而言最近的下一个字符的下标
		last[s[i] - 'a'] = i; //更新s[i]字符最后一次出现的位置
	}

	for (int i = 0; i < len; i++)
		ans += (i - pre[i]) * (ne[i] - i);
	//i点的上一个相同字符距离x,下一个位y, 
	//左边可以取x种(1-x),右边y种,共x*y 
	cout << ans;
	return 0;
}



 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Prudento

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值