力扣828-统计子串中的唯一字符

  |  

摘要: 用前缀和维护前缀的某种状态

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


各位好,在文章 【模板】前缀和与差分 中,我们串讲了前缀和的算法原理和代码模板、在 差分的应用 中,我们学习了用差分的思想解决数列上的一些问题的方法。

在文章 用前缀和的思想快速计算区间上的指标(状态) 中,我们了解了用前缀和的思想快速求区间上的某个指标的做法。

本文我们继续看一个通过预处理前缀状态,然后快速计算我们所求的指标的问题。

$1 题目

我们定义了一个函数 countUniqueChars(s) 来统计字符串 s 中的唯一字符,并返回唯一字符的个数。

例如:s = “LEETCODE” ,则其中 “L”, “T”,”C”,”O”,”D” 都是唯一字符,因为它们只出现一次,所以 countUniqueChars(s) = 5 。

本题将会给你一个字符串 s ,我们需要返回 countUniqueChars(t) 的总和,其中 t 是用前缀和的思想快速计算区间上的指标 s 的子字符串。注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计 s 的所有子字符串中的唯一字符)。

由于答案可能非常大,请将结果 mod 1e9 + 7 后再返回。

提示:

1
2
0 <= s.length <= 10^4
s 只包含大写英文字符

示例 1:

输入: “ABC”
输出: 10
解释: 所有可能的子串为:”A”,”B”,”C”,”AB”,”BC” 和 “ABC”。
其中,每一个子串都由独特字符构成。
所以其长度总和为:1 + 1 + 1 + 2 + 2 + 3 = 10
示例 2:

输入: “ABA”
输出: 8
解释: 除了 countUniqueChars(“ABA”) = 1 之外,其余与示例 1 相同。
示例 3:

输入:s = “LEETCODE”
输出:92

$2 题解

算法: 前缀状态

分别计算每个 s[i] 对答案的贡献,即 s[i] 在几个子串中属于唯一字符。

预处理前缀状态:

1
2
L[i] := [0..i-1] 中距离 s[i] 最近的相同字符的下标
R[i] := [i+1..n-1] 中距离 s[i] 最近的相同字符的下标

枚举 i: ans += (i - L[i]) * (R[i] - i)

代码 (C++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
int uniqueLetterString(string s) {
int n = s.size();
vector<int> L(n, -1), R(n, n);
vector<int> mapping(26, -1);
for(int i = 0; i < n; ++i)
{
L[i] = mapping[s[i] - 'A'];
mapping[s[i] - 'A'] = i;
}
mapping.assign(26, n);
for(int i = n - 1; i >= 0; --i)
{
R[i] = mapping[s[i] - 'A'];
mapping[s[i] - 'A'] = i;
}
const int MOD = 1e9 + 7;
using ll = long long;
int ans = 0;
for(int i = 0; i < n; ++i)
{
ans = (ans + (ll)(i - L[i]) * (ll)(R[i] - i)) % MOD;
}
return ans;
}
};

Share