跳转至

2405. 子字符串的最优划分

题目描述

给你一个字符串 s ,请你将该字符串划分成一个或多个 子字符串 ,并满足每个子字符串中的字符都是 唯一 的。也就是说,在单个子字符串中,字母的出现次数都不超过 一次

满足题目要求的情况下,返回 最少 需要划分多少个子字符串

注意,划分后,原字符串中的每个字符都应该恰好属于一个子字符串。

 

示例 1:

输入:s = "abacaba"
输出:4
解释:
两种可行的划分方法分别是 ("a","ba","cab","a") 和 ("ab","a","ca","ba") 。
可以证明最少需要划分 4 个子字符串。

示例 2:

输入:s = "ssssss"
输出:6
解释:
只存在一种可行的划分方法 ("s","s","s","s","s","s") 。

 

提示:

  • 1 <= s.length <= 105
  • s 仅由小写英文字母组成

解法

方法一:贪心

根据题意,每个子字符串应该尽可能长,且包含的字符唯一,因此,我们只需要贪心地进行划分即可。

我们定义一个二进制整数 \(\textit{mask}\) 来记录当前子字符串中出现的字符,其中 \(\textit{mask}\) 的第 \(i\) 位为 \(1\) 表示第 \(i\) 个字母已经出现过,为 \(0\) 表示未出现过。另外,我们还需要一个变量 \(\textit{ans}\) 来记录划分的子字符串个数,初始时 \(\textit{ans} = 1\)

遍历字符串 \(s\) 中的每个字符,对于每个字符 \(c\),我们将其转换为 \(0\)\(25\) 之间的整数 \(x\),然后判断 \(\textit{mask}\) 的第 \(x\) 位是否为 \(1\),如果为 \(1\),说明当前字符 \(c\) 与当前子字符串中的字符有重复,此时 \(\textit{ans}\) 需要加 \(1\),并将 \(\textit{mask}\) 置为 \(0\);否则,将 \(\textit{mask}\) 的第 \(x\) 位置为 \(1\)。然后,我们将 \(\textit{mask}\) 更新为 \(\textit{mask}\)\(2^x\) 的按位或结果。

最后,返回 \(\textit{ans}\) 即可。

时间复杂度 \(O(n)\),其中 \(n\) 为字符串 \(s\) 的长度。空间复杂度 \(O(1)\)

1
2
3
4
5
6
7
8
9
class Solution:
    def partitionString(self, s: str) -> int:
        ans, mask = 1, 0
        for x in map(lambda c: ord(c) - ord("a"), s):
            if mask >> x & 1:
                ans += 1
                mask = 0
            mask |= 1 << x
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    public int partitionString(String s) {
        int ans = 1, mask = 0;
        for (int i = 0; i < s.length(); ++i) {
            int x = s.charAt(i) - 'a';
            if ((mask >> x & 1) == 1) {
                ++ans;
                mask = 0;
            }
            mask |= 1 << x;
        }
        return ans;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    int partitionString(string s) {
        int ans = 1, mask = 0;
        for (char& c : s) {
            int x = c - 'a';
            if (mask >> x & 1) {
                ++ans;
                mask = 0;
            }
            mask |= 1 << x;
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func partitionString(s string) int {
    ans, mask := 1, 0
    for _, c := range s {
        x := int(c - 'a')
        if mask>>x&1 == 1 {
            ans++
            mask = 0
        }
        mask |= 1 << x
    }
    return ans
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function partitionString(s: string): number {
    let [ans, mask] = [1, 0];
    for (const c of s) {
        const x = c.charCodeAt(0) - 97;
        if ((mask >> x) & 1) {
            ++ans;
            mask = 0;
        }
        mask |= 1 << x;
    }
    return ans;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
impl Solution {
    pub fn partition_string(s: String) -> i32 {
        let mut ans = 1;
        let mut mask = 0;
        for x in s.chars().map(|c| (c as u8 - b'a') as u32) {
            if mask >> x & 1 == 1 {
                ans += 1;
                mask = 0;
            }
            mask |= 1 << x;
        }
        ans
    }
}

评论