LC 1759. 统计同构子字符串的数目

题目描述

这是 LeetCode 上的 1759. 统计同构子字符串的数目 ,难度为 中等

给你一个字符串 s,返回 s 中 同构子字符串 的数目。

由于答案可能很大,只需返回对 $10^9 + 7$ 取余 后的结果。

同构字符串的定义为:如果一个字符串中的所有字符都相同,那么该字符串就是同构字符串。

子字符串是字符串中的一个连续字符序列。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:s = "abbcccaa"

输出:13

解释:同构子字符串如下所列:
"a" 出现 3 次。
"aa" 出现 1 次。
"b" 出现 2 次。
"bb" 出现 1 次。
"c" 出现 3 次。
"cc" 出现 2 次。
"ccc" 出现 1 次。
3 + 1 + 2 + 1 + 3 + 2 + 1 = 13

示例 2:
1
2
3
4
5
输入:s = "xy"

输出:2

解释:同构子字符串是 "x""y"

示例 3:
1
2
3
输入:s = "zzzzz"

输出:15

提示:

  • $1 <= s.length <= 10^5$
  • s 由小写字符串组成

双指针 + 数学

根据题意,我们需要找出 s 中所有字符相同的连续段。

假设 $s[i…(j - 1)]$ 为某个连续段,长度为 $m$,根据「等差数列求和」可知该连续段所能提供的同构字符串数量为 $\frac{(1 + m) \times m}{2}$。

具体的,我们可以从前往后扫描 s,假设当前处理到的位置为 i,将其看作连续段的左端点,然后从 i 出发找到当前最长连续段的右端点 j - 1,统计 $s[i…(j - 1)]$ 所能贡献同构字符串数量,并调整下个发起点为 $i = j$ 以扫描下一个连续段。

Java 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int countHomogenous(String s) {
int n = s.length(), MOD = (int)1e9+7;
long ans = 0;
for (int i = 0; i < n; ) {
int j = i;
while (j < n && s.charAt(j) == s.charAt(i)) j++;
long cnt = j - i;
ans += (1 + cnt) * cnt / 2;
ans %= MOD;
i = j;
}
return (int) ans;
}
}

C++ 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int countHomogenous(string s) {
int n = s.size(), MOD = 1e9+7;
long ans = 0;
for (int i = 0; i < n; ) {
int j = i;
while (j < n && s[j] == s[i]) j++;
long cnt = j - i;
ans += (1 + cnt) * cnt / 2;
ans %= MOD;
i = j;
}
return (int) ans;
}
};

Python3 代码:
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def countHomogenous(self, s: str) -> int:
n, mod, i, ans = len(s), 1e9+7, 0, 0
while i < n:
j = i
while j < n and s[j] == s[i]:
j += 1
cnt = j - i
ans += (cnt + 1) * cnt / 2
ans %= mod
i = j
return int(ans)

TypeScript 代码:
1
2
3
4
5
6
7
8
9
10
11
12
function countHomogenous(s: string): number {
let n = s.length, mod = 1e9+7, ans = 0
for (let i = 0; i < n; ) {
let j = i
while (j < n && s.charAt(j) == s.charAt(i)) j++
const cnt = j - i
ans += (1 + cnt) * cnt / 2
ans %= mod
i = j
}
return ans
}

  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.1759 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。