leetcode-master/problems/0763.划分字母区间.md

180 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 763.划分字母区间
[力扣题目链接](https://leetcode-cn.com/problems/partition-labels/)
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
* 输入S = "ababcbacadefegdehijhklij"
* 输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
提示:
* S的长度在[1, 500]之间。
* S只包含小写字母 'a' 到 'z' 。
## 思路
一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。
题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?
如果没有接触过这种题目的话,还挺有难度的。
在遍历的过程中相当于是要找每一个字母的边界,**如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了**。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
* 统计每一个字符最后出现的位置
* 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
如图:
![763.划分字母区间](https://img-blog.csdnimg.cn/20201222191924417.png)
明白原理之后,代码并不复杂,如下:
```CPP
class Solution {
public:
vector<int> partitionLabels(string S) {
int hash[27] = {0}; // i为字符hash[i]为字符出现的最后位置
for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
hash[S[i] - 'a'] = i;
}
vector<int> result;
int left = 0;
int right = 0;
for (int i = 0; i < S.size(); i++) {
right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
if (i == right) {
result.push_back(right - left + 1);
left = i + 1;
}
}
return result;
}
};
```
* 时间复杂度:$O(n)$
* 空间复杂度:$O(1)$使用的hash数组是固定大小
## 总结
这道题目leetcode标记为贪心算法说实话我没有感受到贪心找不出局部最优推出全局最优的过程。就是用最远出现距离模拟了圈字符的行为。
但这道题目的思路是很巧妙的,所以有必要介绍给大家做一做,感受一下。
## 其他语言版本
### Java
```java
class Solution {
public List<Integer> partitionLabels(String S) {
List<Integer> list = new LinkedList<>();
int[] edge = new int[26];
char[] chars = S.toCharArray();
for (int i = 0; i < chars.length; i++) {
edge[chars[i] - 'a'] = i;
}
int idx = 0;
int last = -1;
for (int i = 0; i < chars.length; i++) {
idx = Math.max(idx,edge[chars[i] - 'a']);
if (i == idx) {
list.add(i - last);
last = i;
}
}
return list;
}
}
```
### Python
```python
class Solution:
def partitionLabels(self, s: str) -> List[int]:
hash = [0] * 26
for i in range(len(s)):
hash[ord(s[i]) - ord('a')] = i
result = []
left = 0
right = 0
for i in range(len(s)):
right = max(right, hash[ord(s[i]) - ord('a')])
if i == right:
result.append(right - left + 1)
left = i + 1
return result
```
### Go
```go
func partitionLabels(s string) []int {
var res []int;
var marks [26]int;
size, left, right := len(s), 0, 0;
for i := 0; i < size; i++ {
marks[s[i] - 'a'] = i;
}
for i := 0; i < size; i++ {
right = max(right, marks[s[i] - 'a']);
if i == right {
res = append(res, right - left + 1);
left = i + 1;
}
}
return res;
}
func max(a, b int) int {
if a < b {
a = b;
}
return a;
}
```
### Javascript
```Javascript
var partitionLabels = function(s) {
let hash = {}
for(let i = 0; i < s.length; i++) {
hash[s[i]] = i
}
let result = []
let left = 0
let right = 0
for(let i = 0; i < s.length; i++) {
right = Math.max(right, hash[s[i]])
if(right === i) {
result.push(right - left + 1)
left = i + 1
}
}
return result
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>