力扣 每日一题 927. 三等分【难度:困难,rating: 1994】(思维+后缀字符串+bitset技巧)

题目链接

https://leetcode.cn/problems/three-equal-parts/

题目来源于:第107场周赛 Q3 rating: 1994

思路

为了方便表述,本文将数组或子数组称之为“字符串”或“子串”。

本题的关键在于发现:

  1. 字符串能被三等分 => 1的总个数必须是3的倍数。必须满足这个必要条件
  2. 第3个子串的结束位置被固定在串的末尾,那么第3个子串的后缀0的个数suffix_zero_count,能用于限制前面第1、2个子串的后缀0个数。因为第3个子串的后缀0是固定的,而前面的两个子串的后缀0是可以往后调整的,只要它们的后缀0个数>=suffix_zero_count,多余的后缀0就可以划分给下一个子串作为前导0。通过这种调整,确定了第1、2个子串的结束位置,同时也确定了第2、3个子串的开始位置(第1个子串的开始位置被固定在串的开头)。
  3. 最后直接得到三个子串的起始和终点位置,依次比对是否相等即可。

放一个图便于理解:
在这里插入图片描述

代码

先切分得到string,再用其初始化bitset,bitset能直接判断是否相等(包括前导0也没有问题)。

class Solution {
public:
    vector<int> threeEqualParts(vector<int>& arr) {
        int sz=arr.size();
        vector<int> ans;
        string str;
        int count=0; // 1的总个数
        for(auto &x:arr){
            if(x==1){
                count++;
            }
            str+=(x+'0'); // arr to string
        }
        // 全是0,没有1,可以任意划分,只要不超过边界即可
        if(count==0){
            return {0,sz-1};
        }
        // 1的个数不是3的倍数,划分失败
        if(count%3!=0){
            return {-1,-1};
        }
        int ave_count=count/3; // 每个子串含有的1的个数
        int end_pos1=-1,start_pos2=-1,end_pos2=-1,start_pos3=-1,end_pos3=-1;
        int now_count=0;
        for(int i=0;i<sz;i++){
            if(arr[i]==1){
                now_count++;
                if(now_count==ave_count){
                    end_pos1=i; // 第1个子串中最后一个1的位置
                }
                if(now_count==ave_count+1){
                    start_pos2=i; // 第2个子串中第一个1的位置
                }
                if(now_count==ave_count*2){
                    end_pos2=i; // 第2个子串中最后一个1的位置
                }
                if(now_count==ave_count*2+1){
                    start_pos3=i; // 第3个子串中第一个1的位置
                }
                if(now_count==(ave_count*3)){
                    end_pos3=i; // 第3个子串中最后一个1的位置
                }
            }
        }

        // 第3个子串后缀0的区间[end_pos3+1, sz)
        int suffix_zero_count=sz-end_pos3-1; // 第3个子串的后缀0个数
        if(start_pos3-end_pos2-1<suffix_zero_count || start_pos2-end_pos1-1<suffix_zero_count){
            // 第2个子串后缀0的区间[end_pos2+1,start_pos3)
            // 第1个子串后缀0的区间[end_pos1+1,start_pos2)
            // 若第1个子串或第2个子串的后缀0个数不够第3个子串的后缀0个数,则划分失败
            return {-1,-1};
        }
        
        // 切分得到s1
        int suffix_end_pos1=end_pos1+suffix_zero_count; // s1加上后缀0,调整s1的结束位置
        assert(suffix_end_pos1<start_pos2);
        string s1=str.substr(0,suffix_end_pos1+1); // [0,suffix_end_pos1]
		
        // 切分得到s2
        int suffix_end_pos2=end_pos2+suffix_zero_count; // s2加上后缀0,调整s2的结束位置
        assert(suffix_end_pos2<start_pos3);
        string s2=str.substr(start_pos2,suffix_end_pos2-start_pos2+1); // [start_pos2,suffix_end_pos2]
		
        // 切分得到s3
        string s3=str.substr(start_pos3); // [start_pos3,sz)

        bitset<30000> t1(s1);
        bitset<30000> t2(s2);
        bitset<30000> t3(s3);
        if(t1==t2&&t2==t3){ // bitset直接判断是否相等
            return {suffix_end_pos1,suffix_end_pos2+1};
        }else{
            return {-1,-1};
        }
    }
};

/*
[1,0,1,0,1]
ans: [0,3]

[1,1,1,0,0,1,1,0,1,0,1,1,1,1,1,1]
ans: [-1,-1]
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

nefu-ljw

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

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

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

打赏作者

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

抵扣说明:

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

余额充值