字符串的总引力
字符串的 引力 定义为:字符串中 不同 字符的数量。
- 例如,
"abbca"的引力为3,因为其中有3个不同字符'a'、'b'和'c'。
给你一个字符串 s ,返回 其所有子字符串的总引力 。
子字符串 定义为:字符串中的一个连续字符序列。
示例 1:
输入: s = "abbca"
输出: 28
解释: "abbca" 的子字符串有:
- 长度为 1 的子字符串:"a"、"b"、"b"、"c"、"a" 的引力分别为 1、1、1、1、1,总和为 5 。
- 长度为 2 的子字符串:"ab"、"bb"、"bc"、"ca" 的引力分别为 2、1、2、2 ,总和为 7 。
- 长度为 3 的子字符串:"abb"、"bbc"、"bca" 的引力分别为 2、2、3 ,总和为 7 。
- 长度为 4 的子字符串:"abbc"、"bbca" 的引力分别为 3、3 ,总和为 6 。
- 长度为 5 的子字符串:"abbca" 的引力为 3 ,总和为 3 。
引力总和为 5 + 7 + 7 + 6 + 3 = 28 。
示例 2:
输入: s = "code"
输出: 20
解释: "code" 的子字符串有:
- 长度为 1 的子字符串:"c"、"o"、"d"、"e" 的引力分别为 1、1、1、1 ,总和为 4 。
- 长度为 2 的子字符串:"co"、"od"、"de" 的引力分别为 2、2、2 ,总和为 6 。
- 长度为 3 的子字符串:"cod"、"ode" 的引力分别为 3、3 ,总和为 6 。
- 长度为 4 的子字符串:"code" 的引力为 4 ,总和为 4 。
引力总和为 4 + 6 + 6 + 4 = 20 。
提示:
1 <= s.length <= 10^5s由小写英文字母组成
解析
以前没有做过字符串子串的问题,不知道这里有个很通用的思考方式,就是以字符串的结尾作为标杆索引来思考,而不是同时思考起点和终点。 以本题为例,只需要知道以某个位置做为截至位置时候,它的引力即可,不需要考虑起点位置在哪里,因为考虑终点位置的时候就已经把所有不同起点但是终点在这儿的情况都考虑进去了。
class Solution:
def appealSum(self, s: str) -> int:
sum_list = []
pos = defaultdict(lambda: -1)
# 这里为了更清晰,使用defaultdict来表征,也可以用ord()函数把字母转成数字
# pos变成一个数组,用下标来表示对应的字母,pos记录的是这个字母在遍历整个s的过程中,最后一次出现在哪个位置
#比如当我遍历到第100个字符的时候,这个字符是m,那pos[m]表示在100个以前的最后一次出现m的位置是哪里,如果从来没出现过,记为-1,也就是默认值
for i, c in enumerate(s):
tmp = i - pos[c]
# tmp用来记录每当向后遍历一次,会增加出来几个引力值
# 当新增加出来的字符c是以前没有出现过的,则此时的pos是-1
# 当前因为新增加了一个字符c而导致的 到i位为止的引力 比 到i-1位为止的引力,增加了tmp
if i == 0:
sum_list.append(tmp)
else:
sum_list.append(sum_list[i-1] + tmp)
# tmp表示相邻位置因为新出现的字符c而带来的引力值增量
# 那对应位置的引力值是保存在sum_list的第i位
pos[c] = i
# 更新字符c出现的最后一次位置(不是全局最后一次,而是遍历过程中的记录)
ans = sum(sum_list)
# 最终结果就是 到每个位置为止 的引力值 的 总和
return ans
这里面的sum_list的每一个值还需要继续说明一下。 到 每个位置位置 的引力值,也即 以第i位为止的字符串的总引力,比如字符s="code",这时候当我们遍历到字母o,也就是index为1,整个字符串第二个字符的时候,sum_list[1] 表示的是,"co" 和 "o" 的总引力,为3, 当遍历到字母d的时候,sum_list[2]表示的是 "cod", "od", "d"的总引力,为6,sum_list[2]不需要记录"co"的引力值,因为被记录在 sum_list[1]中,它是以"o"结尾的。