Question:![](https://img-blog.csdnimg.cn/f4fcc323db30407cb0a20218b128215e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAV29vZGVubWFu5p2c,size_20,color_FFFFFF,t_70,g_se,x_16)
Solve:
初见题目:
枚举所有子串,计算贡献,要是不打算拿满分,可以敲码跑路了,但我不是那人呐,这题肯定不是那么粗暴,一定有规律
该怎么想呢?
我既然不能去枚举所有子串,那就想着去合并,减少计算过程,这是典型的线性相加变成多项乘法和的思想
然后想:
以每一位的字符为子串子元素的只有那么几个, 那我能不能把每一个位字符的贡献度求出来,然后求个和呢,这样不就得到结果了
想到这,这道题就完成一半了
继续分析:
一个子串中的某一个字符 s[ i ] 贡献为0的条件是什么?
出现相同的字符呗,那我把每一位的字符前一次出现的位置 pre[ i ] 和后一次出现的位置 suf[ i ] 找出来,避开这些地方去构造我想要的子串不就行了
接下来是最后的问题:
对于某个字符s[ i ],我避开其前后出现的位置之后,我就可以利用区间[ pre[i] , suf[i] ]的字符串去构造 s[ i ] 一定有贡献的子串了
那么如何快速的计算出这个子符的贡献呢,如果还是暴力的算,那我前面功夫不就白费了嘛
你这样看
我在这个区间里随便揪出一个含有 s[ i ] 的子串,那这个子串里 s[ i ] 是不是都会产生1的贡献
那 s[ i ] 对整个字符串产生的贡献数不就是前面得到的区间里能够生成的包含 s[ i ] 的子串个数吗
而这个个数,不就是
( i - pre[ i ] ) * ( suf[ i ] - i ) (组合数应该不用解释吧)
Code:
#include <bits/stdc++.h>
using namespace std;
string s;
int pre[100010], suf[100010];
int temp[30];
int main(void)
{
cin >>s;
int len = s.length();
//初始化前---一次出现的位置
for(int i = 0; i < 26; i++) temp[i] = -1;
//更新s[i]前一次出现的位置
for(int i = 0; s[i]; i++){
pre[i] = temp[ s[i]-'a' ];
temp[ s[i] - 'a' ] = i;
}
//初始化后一次出现的位置
for(int i = 0; i < 26; i++) temp[i] = len;
//更新s[i]后一次出现的位置
for(int i = len - 1; i >= 0; i--){
suf[i] = temp[ s[i] - 'a' ];
temp[ s[i] - 'a' ] = i;
}
long long res = 0;
for(int i = 0; i < len; i++){
res += (i - pre[i]) * (suf[i] - i);
}
cout <<res;
return 0;
}