贪心算法 | 763.划分字母区间

·题目描述

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s

返回一个表示每个字符串片段的长度的列表。

示例 1:

复制代码
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。 

示例 2:

复制代码
输入:s = "eccbbbbdec"
输出:[10]

·解题思路

很质朴的想法是,遍历一圈字符串s, 把每个字母出现的起始位置用数组记录下来。再套用之前leetcode 452 用最少数量的箭引爆气球 和 leetcode 435 无重复区间的 思路, 将数组进行规划;

规划分为三步:1.最开始的位置进行排序;2.当后一个数组的开始位置小于前一个数组的结束位置的时候,说明两个数组有重叠,更新数组区间;3.当后一个数组的开始位置大于前一个数组的结束位置的时候,说明两个数组没有重叠区间,需要计算前一个数组的字符串长度

·解题细节:

1.每一个字母都可能出现,因此创建26*2的二维数组,计算每一个出现的字母的开始和结束位置。同时,如何判断该数字是开始坐标还是结束坐标------创建flag数组来标记,若是flag = 0 ,表示之前没有出现过,该坐标为起始坐标,反之则为结束坐标。结束坐标需要不断更新;

复制代码
            int[][] num = new int[26][2];
            int[] flag = new int[26];

            for(int i = 0; i < s.length(); i++){
            int temp  = s.charAt(i) - 'a';
            if(flag[temp] == 0){num[temp][0] = i;flag[temp] = 1;}
            else{ num[temp][1] = Math.max(num[temp][1], i);}
            }

2.对记录的字母坐标按照第一个元素进行排序

复制代码
        Arrays.sort(num, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return Integer.compare(o1[0], o2[0]);
            }
        });
        
        #打印结果
        for(int i = 0; i < num.length; i++){
            System.out.println(num[i][0] + " " + num[i][1]);
        }

3.对坐标数组进行处理,遍历num数组,利用栈来辅助更新区间。当当前数组的开始位置小于栈顶数组的结束位置的时候,说明两个数组有重叠,更新数组区间;反之说明两个数组没有重叠区间,需要计算前一个数组的字符串长度

复制代码
        Stack<int[]> stack = new Stack<int[]>();
        for(int i = 0;i < num.length;i++){
            
 
            
            #当栈为空的时候,先压入
            if(stack.isEmpty()) stack.push(num[i]);
            else{
                int[] point = stack.peek();
                if(num[i][0] < point[1]){
                    int newend = Math.max(point[1], num[i][1]);
                    int[] newpoint = new int[] {point[0],newend};
                    stack.pop();
                    stack.push(newpoint);
                }
                else{
                    int len = stack.peek()[1] - stack.peek()[0] + 1;
                    res.add(len);
 
                    stack.pop();
                    stack.push(num[i]);
                }
            }
        }

4.有可能最后一个子字符串不能满足for循环的收割条件,也就是说栈不为空,这是还需要处理

复制代码
        while(!stack.isEmpty()){
            int[] point = stack.pop();
            if(point[1] == 0) {res.add(1);count += 1;}
            else{
                int len = point[1] - point[0] + 1;
                res.add(len);
            }
        }

5.处理空数组:

由于给出的字符不一定包含26个字母,也就是说num中有很多空数组参与了排序,这时候只需要在遍历的时候,当数组两个元素都是0的时候,continue即可

复制代码
if(num[i][0] == 0 && num[i][1] == 0){continue;}

6.处理单个元素

字符串中可能存在单个元素,分为两种情况:头单个【0,0】和其他【x(x!= 0) , 0】

处理头单个的时候,只需要增加一个count技术,当所有子串的长度小于给定字符串的长度时,说明有头单个元素漏加,只需要在列表结构头部增加 1 ,即可

处理其他单个元素的时候,只需要在遍历num时,当数组第一个元素不为0,而第二元素为0 的时候,将第二个元素变为第一个元素相同值即可

复制代码
            if(num[i][0] == 0 && num[i][1] == 0){continue;}
            if(num[i][0] != 0 &&  num[i][1] == 0){num[i][1] = num[i][0];}

·java代码

复制代码
import java.util.*;

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[][] num = new int[26][2];
        int[] flag = new int[26];
        int count = 0;

        for(int i = 0; i < s.length(); i++){
            int temp  = s.charAt(i) - 'a';
            if(flag[temp] == 0){num[temp][0] = i;flag[temp] = 1;}
            else{ num[temp][1] = Math.max(num[temp][1], i);}
        }

        Arrays.sort(num, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return Integer.compare(o1[0], o2[0]);
            }
        });

        for(int i = 0; i < num.length; i++){
            System.out.println(num[i][0] + " " + num[i][1]);
        }

        List<Integer> res = new ArrayList<Integer>();
        Stack<int[]> stack = new Stack<int[]>();
        for(int i = 0;i < num.length;i++){
            if(num[i][0] == 0 && num[i][1] == 0){continue;}
            if(num[i][0] != 0 &&  num[i][1] == 0){num[i][1] = num[i][0];}
            if(stack.isEmpty()) stack.push(num[i]);
            else{
                int[] point = stack.peek();
                if(num[i][0] < point[1]){
                    int newend = Math.max(point[1], num[i][1]);
                    int[] newpoint = new int[] {point[0],newend};
                    stack.pop();
                    stack.push(newpoint);
                }
                else{
                    int len = stack.peek()[1] - stack.peek()[0] + 1;
                    res.add(len);
                    count += len;
                    stack.pop();
                    stack.push(num[i]);
                }
            }
        }
        while(!stack.isEmpty()){
            int[] point = stack.pop();
            if(point[1] == 0) {res.add(1);count += 1;}
            else{
                int len = point[1] - point[0] + 1;
                res.add(len);
                count += len;
            }
        }
        if(count < s.length()) res.add(0,1);
        return res;
    }
}

public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.partitionLabels("vhaagbqkaq"));
    }
}
相关推荐
2401_858286115 分钟前
CD21.【C++ Dev】类和对象(12) 流插入运算符的重载
开发语言·c++·算法·类和对象·运算符重载
Foyo Designer19 分钟前
【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的国际化:支持多语言的 RESTful API
java·spring boot·redis·后端·spring·缓存·restful
Stuild Stuil20 分钟前
Mysql 字段值批量自增或自减(坐标系数据,(x,y))
java·javascript·mysql
敲敲敲-敲代码25 分钟前
【Kettle安装】Kettle安装过程, 电脑已安装java23,安装Kettle 出现报错:尝试启动 Java 虚拟机(JVM)时失败解决方法
java·kettle
梭七y27 分钟前
【力扣hot100题】(033)合并K个升序链表
算法·leetcode·链表
月亮被咬碎成星星31 分钟前
LeetCode[383]赎金信
算法·leetcode
利明的博客36 分钟前
mediacodec服务启动时加载media_codecs.xml
android·xml·java
葵野寺1 小时前
【多线程】线程池
java·开发语言·java-ee·web app
十六ᵛᵃᵉ1 小时前
day6_FlinkSQL实战
java·redis·ajax
嘉友1 小时前
Redis zset数据结构以及时间复杂度总结(源码)
数据结构·数据库·redis·后端