子串分值和
【题目描述】
对于一个字符串S,我们定义S的分值f(S)为S中出现的不同的字符个数。例如f("aba ')=2,f("abc")= 3,f(“aaa”)=1。现在给定一个字符串 S[0...n-1](长度为n),请你计算对于所有S的非空子串S [i...j](0≤i≤j<n) ,f(S[i.….j])的和是多少。
输入描述
输入一行包含一个由小写字母组成的字符串 S。
其中,1≤n≤10^5。
输出描述
输出一个整数表示答案。
输入输出样例
输入
ababc
输出
28
【问题解析】
一、暴力破解
- 找出所有可能的子串;
- 统计每个子串的分值;
str1 = input()
ans = 0
# 两个for循环找出所有可能的子串
for i in range(len(str1)):
for j in range(i+1,len(str1)+1):
s = str1[i:j]
set1 = set(s) # 用集合去重
ans += len(set1)
print(ans)
超时了,只能通过40%的测试。
二、统计个体贡献
由于f(S)表示S中出现的不同的字符个数,也就是说对于字符串aaa而言,3个a中只有一个a可以对其产生贡献。那么我们不妨将第1个、第2个a的贡献抹去,只计算第3个a的贡献。即当一个字符ch在字符串中出现了多次,我们只计算最靠右的ch对答案的贡献(其它ch在该字符串的贡献都被抹去了)。
【做法】定义right_index表示第i个字符下一次出现的位置。那么要让第i个字符能对答案产生贡献,则包含它的子串的左端点的取值范围必须在[1 , i]之间(即左端点没有限制),右端点的取值范围必须在[i+ 1, right_index - 1]之间(若右端点的取值大于等于right_index,则该字符对该子串贡献将被抹除)。其对答案的贡献为限制左右端点取值范围后所能构造出的子串的个数,即i*(right_index - i)。
如上图,左端点:[0]右端点:[1,4]记在第一个a的贡献;左端点:[0,5]右端点:[6,8]记在第二个a的贡献;左端点:[0,9]右端点:[10]记在第三个a的贡献。
s=input()
ans=0
for i,c in enumerate(s): # 取出索引i和元素C
r=i+1 # 右端点:当前索引的下一个
while r<len(s): # 右端点从当前索引的下一个到末尾进行遍历
if s[r]!=c:r+=1 # 不是相同的字符,右端点向右移一位
else:break # 是相同的字符,结束
ans+=(i+1)*(r-i) # 贡献:左边(所有)*右边(没有相同)。i+1是因为i从0开始
print(ans)