[蓝桥杯 2020 省 AB2] 子串分值

一.题目

题目描述

对于一个字符串 S,我们定义 S 的分值 f(S)S 中恰好出现一次的字符个数。

例如 f(“aba”)=1,f(“abc”)=3, f(“aaa”) = 0

现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S 的非空子串 S[i…j](0 ≤ i ≤ j < n), f(S[i…j]) 的和是多少。

输入格式

输入一行包含一个由小写字母组成的字符串 S

输出格式

输出一个整数表示答案。

数据范围

对于 20% 的评测用例,1 ≤ n ≤ 10
对于 40% 的评测用例,1 ≤ n ≤ 100
对于 50% 的评测用例,1 ≤ n ≤ 1000
对于 60% 的评测用例,1 ≤ n ≤ 10000
对于所有评测用例,1 ≤ n ≤ 100000

输入样例1

ababc

输出样例1

21

二.解释

看完题目,先交手暴力代码,暴力代码直接写就好,后面会给出来,只能过50%。

我们从单独一个字符入手,以 cababcab 为例,第四个字符为 a, 对于这个 a 来说,它对所有只包含这一个 a 的子串有贡献,即所有只包含这一个 a 的子串的 f() 值都有它。

省去其余字符只保留 a ,得 ·a·a··a,下标从 1 开始·。

设所有只包含中间 a 的子串数为 x,中间 a 下标为 K,左一个 a 下标位 L ,有一个 a 下标为 R;

a k a_k ak 左边取子串的首位有 (K - L) 种取法,右边取子串的末位有 (R - K) 种取法;

则:x = (K - L) * (R - K);

这一要注意一下边界情况:
a K a_K aK 为第一个 a 时,我们认为在下标 0 处有一个 a;
a K a_K aK 为最后一个 a 时,我们认为在下标 n + 1 处有一个 a;
这样按照上面的公式算出来的结果才是正确的。

三.代码

AC代码:
暴力代码:
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>

using namespace std;

typedef unsigned long long int64;
const int MaxN = 1e5 + 10;

int InN, InK, Res;
string InS;
unordered_map<char, int> Hash[MaxN];
set<char> C[MaxN];

int main()
{
	cin >> InS;

	for (int i = 0; i < InS.size(); i++)
	{
		unordered_map<char, int> Map;	//从i开始每个字符的出现次数
		Map[InS[i]]++;
		int Count = 1;		//统计当前子串不重复字符的个数
		Res++;		//单独一个字符作为子串的情况
		for (int j = i + 1; j < InS.size(); j++)
		{
			if (Map[InS[j]] == 1) { Res += --Count; }
			else if (Map[InS[j]] > 1) { Res += Count; }
			else { Res += ++Count; }
			Map[InS[j]]++;
		}
	}

	cout << Res;

	return 0;
}
AC代码:
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>

using namespace std;

typedef unsigned long long int64;
const int MaxN = 1e5 + 10;

int64 InN, InK, Res;
string InS;
int L[MaxN], R[MaxN], Pre[30];	//对于下标为i的字符来说:L[i]左一个字串出现的下标,R[i]右一个字串出现的下标,Pre[i]临时存放上一个字符出现的位置

int main()
{
	cin >> InS;
	
	//求L[i]
	for (int i = 1; i <= InS.size(); i++)
	{
		//字符串下标默认从0开始,所以这里要减一
		int Tmp = InS[i - 1] - 'a';
		L[i] = Pre[Tmp];
		Pre[Tmp] = i;
	}
	
	//求R[i]
	for (int i = 0; i < 30; i++) { Pre[i] = InS.size() + 1; }
	for (int i = InS.size(); i > 0; i--)
	{
	//字符串下标默认从0开始,所以这里要减一
		int Tmp = InS[i - 1] - 'a';
		R[i] = Pre[Tmp];
		Pre[Tmp] = i;
	}
	
	for (int i = 1; i <= InS.size(); i++)
	{
		Res += (int64)(i - L[i]) * (R[i] - i);
	}

	cout << Res;

	return 0;
}
  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值