leetcode-763. 划分字母区间

贪心算法

划分字母区间


题目详情

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表


示例:

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

我的代码:

C++

class Solution 
{
public:
    vector<int> partitionLabels(string s) 
    { 
        int last[26],len=s.size();
        for(int i=0;i<len;++i)              
        {
            last[s[i]-'a']=i;
        }
        
        int start=0,end=0;                  
        vector<int> ans;                    
        for(int i=0;i<len;++i)
        {
           
            end=max(end,last[s[i]-'a']);    
            if(i==end)                      
            {                               
                ans.push_back(end-start+1); 
                start=end+1;                

            }
        }
       
        return ans;
    }
};

Java

class Solution {
    public List<Integer> partitionLabels(String s) {

        //利用last数组来存储26个字母的最后出现位置的下标
        int last[] = new int[26];
        char ss[] = s.toCharArray(); 
        int len = s.length();
        //遍历一次来统计每个字母的最后位置下标
        for (int i = 0; i < len; ++i){
            last[ss[i] - 'a'] = i;
        }
        //start:待切割片段的头  end:尾
        int start = 0, end = 0;
        List<Integer> ans = new LinkedList<>(); 
        for (int i = 0; i < len; ++i){
            /*
             * 这里更新尾的思路是:
             * 比较 已经决定好的尾 和 遍历到的字母的last位置
             * 哪个更远,就将尾(切割位置)更新为它
             */
            end = Math.max(end, last[ss[i]-'a']);
            //遍历到i==end了,是时候切割了,为什么呢?因为贪心啊!
            //越早切割,片段越多!
            if (i == end){ 
                ans.add(end - start + 1); //片段长度注意是end-start+1
                start = end + 1;          //更新切割头  
            }
        }

        return ans;

    }
}

方法二:转化为区间问题

可以统计出现字母的头尾位置,存入一个二维数组,从而转变为不重叠区间问题,更能体现贪心算法

class Solution {

    //将字母转换为一个个的区间
    public int[][] findPartitions(String s){

        
        int[][] hash = new int[26][2]; //26个字母,区间头尾2

        for (int i = 0; i < s.length(); ++i){
            //提取出i处的字符c来处理
            char c = s.charAt(i);
            //未出现过,则记录此处为c的头
            if (hash[c - 'a'][0] == 0) hash[c - 'a'][0] = i;

            hash[c - 'a'][1] = i; //出现一次就更新一次尾巴
            //注意一定要单独处理第一个字母的头为0
            //如果不处理,第一个字母的头肯定会弄成第二次出现的地方
            hash[s.charAt(0) - 'a'][0] = 0;
        }

        //↓h来统计有多少个字母出现过,这样就不用记录没有出现的字母了,节省空间

        //temp每次临时记录一个字母的头和尾
        List<Integer> temp = new ArrayList<>();
        //h存储所有字母的头尾
        List<List<Integer>> h = new LinkedList<>();
        //遍历,将所有字母的头尾存入h
        for (int i = 0; i < 26; ++i){
            temp.clear();
            temp.add(hash[i][0]);
            temp.add(hash[i][1]);
            h.add(new ArrayList<>(temp));
        }
        //开辟出 出现过的字母数量的空间即可
        //即过滤掉没出现的字母,就不用记录26个了
        int[][] res = new int[h.size()][2];
        for (int i = 0; i < h.size(); ++i){
            List<Integer> list = h.get(i);
            res[i][0] = list.get(0);
            res[i][1] = list.get(1);
        }

        return res;


    }




    public List<Integer> partitionLabels(String s) {

        //字符串转换成一个个的区间,从而转变成区间问题
        int[][] partitions = findPartitions(s);
        List<Integer> res = new ArrayList<>();
        Arrays.sort(partitions, (a,b) -> a[0] < b[0]);//按头排序
        int right = partitions[0][1]; //从第一个区间开始[0,partitions[0][1]]
        int left = 0;
        for (int i = 0; i < partitions.length; ++i){
            //一旦下一区间的头小于当前区间尾了(两个区间无重叠)
            //则可以切割一段
            //这里i从0开始是为了记录上最左边的一段
            if (partitions[i][0] > right){
                res.add(right - left + 1); 
                left = partitions[i][0]; 
            }
            //每次更新为最大右边界(保证切割下来的段中字母所有都在)
            right = Math.max(right, partitions[i][1]);
        }
        //记录上最右边剩余的一段
        res.add(right - left + 1);

        return res;
    

    }
}

这个方法就和
leetcode-435.无重叠区间
leetcode-452.用最少数量的箭引爆气球
思想完全一样了

涉及知识点:

  1. 贪心算法

顾名思义,贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最
后得到的结果是全局最优的。

思路:

统计每一个字符最后出现的位置
从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ggaoda

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值