题目:
对于一个字符串 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%20% 的评测用例,1≤n≤10;
对于 40%40% 的评测用例,1≤n≤100;
对于 50%50% 的评测用例,1≤n≤1000;
对于 60%60% 的评测用例,1≤n≤10000;
对于所有评测用例,1≤n≤100000。
输入样例:
ababc
输出样例:
21
样例说明
所有子串 f值如下:
a 1
ab 2
aba 1
abab 0
ababc 1
b 1
ba 2
bab 1
babc 2
a 1
ab 2
abc 3
b 1
bc 2
c 1
题目分析:
这个题目可以采用暴力法和贡献法,这里我们采用贡献法来写代码;
我们从后面往前面分析,首先是例如有一个a(位置为i),它左边的第一个a(假设位置为l),它右边的第一个a(假设位置为r),所以有sum=(i-l)*(r-l)种可能,所以它在总的个数里面的个数为sum,同理我们只需要求每一个字符左右两端的第一个相同字符的位置,左边没有则为0,右边没有相同的则为n+1,再把所有字符的这个sum相加就是总的个数。
代码如下:(代码里面带有注释,看不懂可以发评论或者私聊我给你讲解)
#include<stdio.h>
#include<string.h>
char arr[100005];
typedef long long LL;//因为个数可能超过int的存储范围所以采用LL存储数据
int l[100005],r[100005],p[26]={0};//l数组用于保存它左边第一个相同的元素,r数组用于保存右边第一个相同元素的位置(以这个字符为起点)
//p数组初始为0,因为这个时候其左边是没有相同的元素的
int main()
{
LL sum=0;
scanf("%s",arr+1);//将数据保存在arr数组里面
int n=strlen(arr+1);//记录字符的总的个数
for(int i=1;i<=n;i++)
{
int t=arr[i]-'a';//找到这个字符对应的p数组的位置
l[i]=p[t];//将这个元素左边第一个相同元素的位置保存在l数组里面
p[t]=i;//修改p[t],因为对于下一个相同的元素,它左边第一个相同元素为这个元素的位置
}
for(int i=0;i<26;i++)p[i]=n+1;//修改p[t],用于保存其右边相同元素的位置
for(int i=n;i>=1;i--)
{
int t=arr[i]-'a';//找到这个字符对应的p数组的位置
r[i]=p[t];//将这个元素右边第一个相同元素的位置保存在r数组里面
p[t]=i;//修改p[t],因为对于下一个相同的元素,它右边第一个相同元素为这个元素的位置
}
for(int i=1;i<=n;i++)
sum+=(LL)(i-l[i])*(r[i]-i);//计算总和
printf("%lld",sum);//输出个数
return 0;
}