贡献法(总体之和等于个体贡献度之和)(蓝桥杯)

170 阅读2分钟

贡献法的这种题目看起来比较复杂,暴力代码时间复杂度也是在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]) 的和是多少。

image.png

输入样例:

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)