763、划分字母空间

1、题目描述

. - 力扣(LeetCode)

2、分析

要求: 

(1)让字符串划分尽可能多的片段;

(2)同一个字母只能在一个片段中出现,不能出现在多个片段中。

思路如下:

(1)从前往后每出现一个字符,就找到这个字符最后一次出现的位置;显然,该片段的右边界至少要到此字符最后一次出现的位置。

(2)接下来要做的事情就是遍历区间内的所有字符,利用每个新字符在最右侧出现的位置去扩宽该片段的右边界。

(3)什么时候记录结果呢?

①遍历到了end此时可以收集一波结果,并更新下一片段的beg。

②另外一个容易遗漏,即如果end已经是s的最后一个元素此时我们也可以直接收集结果。

(4)是否可以优化性能?

①显然我们对所有区间的字符都去调用find_last_of是不明智的,因为很多字符会重复出现;此时我们搞个map记录下即可。

②另外,只对新出现的字符去尝试更新本片段的右边界即可,没有出现过的字符没必要继续用起最后出现的位置更新右边界了。

(5)其实我们还可以继续做优化。我们可以通过遍历一遍字符串就得到每个字符最后出现的位置,记录在数组里面即可(总共26个字母)。

(6)以"ababcbacadefegdehijhklij"为例。

        我们先以int hash[27]记录每个字母最右侧出现的位置。退出条件和前面分析的一样,遍历过程就是不断扩宽end位置的过程。

①当我们遍历到第一个字符a的时候该片段右边界至少要到小标8,好在0~8中出现的字符并没有谁最后一次出现的位置超过8,所以第一个片段就是[0,8];

②继续遍历下面的d,此时该片段的右边界至少到下标14;但是在遍历到[9,14]中的小标10的e时候,就不得不把右边界扩展到包含下标为15的e。以此类推。

3、实现代码

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>

using namespace std;

//能跑过,但是性能仅击败5%
/*
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> res;
        map<char,int> char_pos_map;
        int beg = 0, end = 0;
        for(int i = 0; i < s.size(); i++){
            end = max(end, (int)s.find_last_of(s[i]));
            if(end == s.size() || i == end ){
                res.push_back(end - beg + 1);
                beg = i + 1;
            }
        }
        return res;
    }
};
*/

//击败 11%了,看起来还可以继续优化。
/*
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> res;
        map<char,int> char_pos_map;
        int beg = 0, end = 0;
        for(int i = 0; i < s.size(); i++){
            //对于每个字符我们都尝试从map中获取其右边界位置,如果没有我们就执行find_last_of更新进去。
            if(char_pos_map.find(s[i]) == char_pos_map.end()){
                char_pos_map[s[i]] = s.find_last_of(s[i]);
            }
            end = max(end, char_pos_map[s[i]]);
            if(end == s.size() || i == end ){
                res.push_back(end - beg + 1);
                beg = i + 1;
            }
        }
        return res;
    }
};
*/

//这个就击败50%了。
/*
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> res;
        map<char,int> char_pos_map;
        int beg = 0, end = 0;
        for(int i = 0; i < s.size(); i++){
            //对于每个字符我们都尝试从map中获取其右边界位置,如果没有我们就执行find_last_of更新进去。
            if(char_pos_map.find(s[i]) == char_pos_map.end()){
                char_pos_map[s[i]] = s.find_last_of(s[i]);
                end = max(end, char_pos_map[s[i]]); //只有出现新字符的时候我们才尝试更新右边界,已经出现的那些字符再去更新没有意义。
            }
            if(end == s.size() || i == end ){
                res.push_back(end - beg + 1);
                beg = i + 1;
            }
        }
        return res;
    }
};
*/

//性能最高
class Solution {
public:
    vector<int> partitionLabels(string s) {
        //遍历一遍字符串得到每个字符最后出现的位置,记录在数组里面即可。
        int hash[27]  = {0};
        for(int i = 0; i < s.size(); i++){
            hash[s[i]-'a'] = i;
        }
        vector<int> res;
        int beg = 0, end = 0;
        for(int i = 0; i < s.size(); i++){
            end = max(end, hash[s[i]-'a']);
            if(end == s.size() || i == end ){
            //if( i == end){ 
                res.push_back(end - beg + 1);
                beg = i + 1;
            }
        }
        return res;
    }
};


int main()
{
    Solution s1;
    string str = "ababcbacadefegdehijhklij";
    //string str = "eccbbbbdec";
    vector<int> res = s1.partitionLabels(str);
    for(int len: res){
        cout << len << ",";
    }
    cout << endl;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
等价类划分法是一种常用的测试用例设计方法,它将系统的输入域划分为等价类,然后从每个等价类中选择测试用例。这样可以最大程度地覆盖输入空间,并减少冗余的测试用例。 以下是一个简单的例子,以帮助理解等价类划分法的应用: 假设我们要测试一个登录功能,其中用户名和密码是必填项,且用户名只能包含字母和数字,密码长度必须在6到12个字符之间。 根据等价类划分法,我们可以将输入域划分为以下等价类: 1. 用户名为空 2. 密码为空 3. 用户名包含非法字符(如特殊符号) 4. 密码长度小于6个字符 5. 密码长度大于12个字符 6. 用户名和密码符合要求(合法的输入) 接下来,我们从每个等价类中选择一个测试用例: 1. 用户名为空: - 输入:用户名为空,密码为有效值 - 预期结果:登录失败,提示用户名不能为空 2. 密码为空: - 输入:用户名为有效值,密码为空 - 预期结果:登录失败,提示密码不能为空 3. 用户名包含非法字符: - 输入:用户名包含特殊字符,密码为有效值 - 预期结果:登录失败,提示用户名包含非法字符 4. 密码长度小于6个字符: - 输入:用户名为有效值,密码长度小于6个字符 - 预期结果:登录失败,提示密码长度不能小于6个字符 5. 密码长度大于12个字符: - 输入:用户名为有效值,密码长度大于12个字符 - 预期结果:登录失败,提示密码长度不能大于12个字符 6. 用户名和密码符合要求: - 输入:用户名和密码都符合要求的合法值 - 预期结果:登录成功 通过以上的测试用例,我们可以覆盖到所有的等价类,并验证系统对各种情况的处理是否正确。 当然,实际应用中可能还有更多的等价类,需要根据具体情况进行划分和选择。这里只是一个简单的示例,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

焱齿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值