贡献法的这种题目看起来比较复杂,暴力代码时间复杂度也是在On2的时间上,在蓝桥杯和oi中,肯定要将时间复杂度控制在O(log n)或者O(n)的时间复杂度内。这个题一般需要将题目过程拆解成单个元素的子问题,去分析单个元素在整个问题当中的贡献度。 感觉像是在dp,将大问题拆分为子问题解决然后整合一下,
1,先考虑题目的子问题思路 2,如何计算子问题对大问题的贡献
子串分值
对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中恰好出现一次的字符个数。
例如 f(“aba”)=1,f(“abc”)=3, f(“aaa”)=0。
现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S的非空子串 Si…j, f(S[i…j]) 的和是多少。
输入样例:
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
1.统计每个字母出现的位置 2.计算每个位置的贡献
怎么计算贡献?
举个例子:
***a**a***a****
第二个a对答案的贡献就是:
*a
**a 即与前一个a位置差再减一
a*
a**
a*** 即与后一个a位置差再减一
*a*
*a**
*a***
**a*
**a**
**a*** 即上述两结果的乘积
三部分相加即是每个位置对答案的贡献(别忘了还要加上自身,可以在最后加上长度即可)
优化:
每个位置的贡献只与前后两个位置有关,可以优化成两个一维的数组,分别记录前后两个位置,考虑累加字符串中的每个字符对答案的贡献以此来计算答案,对于第i个字符,记li表示其左边最近的与它相等的字符的位置,ri表示其右边最近的与它相等的字符的位包含第置,则i个字符的子串数量为(i−li)×(ri−i)(−)×(−),此即为第i个字符的贡献
ac代码
s = input() # 输入
ans, n = 0, len(s) # 答案 字符串长度
pos = [[] for _ in range(26)] # 存储每个字符的下标
for i, c in enumerate(s):
pos[ord(c) - ord('a')].append(i)
for i in range(26):
pos[i] = [-1] + pos[i] + [n] # 首位分别加上哨兵节点 -1, n
for idx in pos:
for i in range(1, len(idx) - 1):
ans += (idx[i] - idx[i - 1]) * (idx[i + 1] - idx[i]) # 累加位于 idx[i] 的字符对答案做出的贡献
print(ans)