
2026-04-13:不同首字母的子字符串数目。用go语言,给定一个只包含小写字母的字符串 s
你需要把它切分成若干个连续、非空的子串(覆盖整个字符串,且不重叠)。
目标是:让子串的数量尽可能多,并满足限制——每个子串的起始字符都必须各不相同,也就是说任意两个子串的第一个字母不能相同。
请输出满足上述条件时,子串的最大数量。
1 <= s.length <= 100000。
s 仅由小写英文字母组成。
输入: s = "abcd"。
输出: 4。
解释:
可以将 "abcd" 划分为 "a"、"b"、"c" 和 "d"。
每个子字符串都以不同的字符开头。因此,答案是 4。
题目来自力扣3760。
我们需要把给定的纯小写字母字符串,分割成连续、不重叠、非空的子串,满足两个关键条件:
补充:小写字母一共只有26个(a-z),因此最大可能的子串数量永远不会超过26,这是核心限制。
要让子串数量最多,最优策略是:尽可能把每个字符单独切分为一个子串,只要这个字符还没有被用作过子串的起始字符。 因为一旦某个字母作为子串开头使用过一次,后续就不能再用了,所以我们需要记录已经使用过的起始字母,遍历字符串时逐个判断。
创建一个标记集合/标记位,专门用来记录已经被用作子串起始的字母,初始状态为空,没有任何字母被使用。 同时初始化一个计数器,用来统计最终的子串数量,初始值为0。
我们按顺序处理字符串中的每一个字符,判断当前字符是否能作为新子串的起始字符:
a:a 是否在已使用的起始字母集合中 → 未使用;a 单独切分为一个子串;a 加入已使用集合;b:b 是否在已使用的起始字母集合中 → 未使用;b 单独切分为一个子串;b 加入已使用集合;c:c 是否在已使用的起始字母集合中 → 未使用;c 单独切分为一个子串;c 加入已使用集合;d:d 是否在已使用的起始字母集合中 → 未使用;d 单独切分为一个子串;d 加入已使用集合;整个字符串遍历完成,计数器的值就是最大子串数量,最终结果为4。
如果字符串出现重复字母,例如 s="abac":
a:未使用,切分,计数=1,标记a;b:未使用,切分,计数=2,标记b;a:已标记使用过,不能作为新子串开头,必须和前一个子串合并(即ba合并为一个子串);c:未使用,切分,计数=3,标记c;
最终结果为3。.
package main
import (
"fmt"
"math/bits"
)
func maxDistinct(s string)int {
set := 0
for _, c := range s {
set |= 1 << (c - 'a')
}
return bits.OnesCount(uint(set))
}
func main() {
s := "abcd"
result := maxDistinct(s)
fmt.Println(result)
}

.
# -*-coding:utf-8-*-
def max_distinct(s: str) -> int:
bitmask = 0
for c in s:
bitmask |= 1 << (ord(c) - ord('a'))
return bin(bitmask).count('1')
if __name__ == "__main__":
s = "abcd"
result = max_distinct(s)
print(result)
.
#include <iostream>
#include <string>
#include <bit>
int maxDistinct(const std::string& s) {
int set = 0;
for (char c : s) {
set |= 1 << (c - 'a');
}
return std::popcount(static_cast<unsigned int>(set));
}
int main() {
std::string s = "abcd";
int result = maxDistinct(s);
std::cout << result << std::endl;
return0;
}
