这里说明一下我的做题思路:首先该题对于我来说有一定的难度, 我一开始的想法如下
#include<bits/stdc++.h>
using namespace std;
int check(string str)//计算分值
{
}
int main()
{
int sum=0;
string str2;
string str1;
cin>>str1;
for(int i=0;i<str1.length();i++)//双重循环
{
str2=str1[i];
for(int j=i+1;j<str1.length();j++)
{
sum+=check(str2);//计算对应字串的分值
str2+=str1[j];//模拟样例分析一个一个字符加上去
}
str2.clear();
//清空
}
return 0;
}
可惜最后也是未能写出完整代码,最后和这道题耗了将近一个小时,还是上网找了题解来看才有了新思路,下面介绍两种解题思路:
1.暴力法(无法通过全部样例,会超时!)
- 【定义变量】用一个字母表存储每个字符出现的次数,用num存储以i(0<= i < 字符串长度)开始的子串的f值,用sum存储子串f值的和。(字母表和num每次 i 改变后要重置!)
- 【双重循环】子串的起点为 i(0<= i < 字符串长度),固定起点,终点从起点开始向后移动,每次移动会有一个新的字符增加,我们给相应的字母表中该字符对应的元素elem+1,然后我们就可以根据该元素的值判断当前子串的f值sum,若elem = 1,说明新增一个恰好只出现一次的字符,则num++;然后sum+=num即可。
- 【得出结果】循环结束后,sum值其实就出来了,输出即可。
#include <iostream>
#include <vector>
#include <string>
#include "string.h"
#include <algorithm>
using namespace std;
int main()
{
string str;
cin >> str;
long long len = str.length();
long long alphabet[27], num, sum =0;
for (long long i = 0; i < len; i++)//子串起点
{
num = 0;
memset(alphabet, 0, sizeof(alphabet));
for (long long j = i; j < len; j++)
{
int k = str[j] - 'a';
alphabet[k]++;
if (alphabet[k] == 1)//新增一个恰好只出现一次的字符
num++;
sum += num;
}
}
cout << sum;
return 0;
}
2.计算贡献值法
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
char s[N];
ll f[N]; //预处理会爆int
int last[30];
int main(){
scanf("%s",s+1);
int n = strlen(s+1);
ll ans = 0;
//预处理没有任何重复的情况;
for(int i=1;i<=n-i+1;i++)f[i] = f[n-i+1] = (n-i+1)*(ll)i; //注意乘的时候一定要转long long
//统计总贡献,并去重
for(int i = 1;i<=n;i++){
int t = s[i]-'a';
if(!last[t]){ //判断有无重复部分
ans += f[i];
}else{
ans += f[i] - f[i]/i*last[t]; //减去重复部分;
}
last[t] = i; //记录当前字母最后出现位置
}
printf("%lld",ans);
return 0;
}
以上笔记分别借鉴于(1条消息) 【2020蓝桥杯省赛】【字符串】子串分值(详解!)(两种解法)_NOT_TODAY1的博客-CSDN博客_蓝桥杯子串分值和(1条消息) 十一届蓝桥杯 子串分值和(一看就会)_littletomorrow的博客-CSDN博客_蓝桥杯子串分值和