hackerrank-----Bear and Steady Gene

题意很简单,给一个最大为50w长度的字符串,字符串只由ACGT四种字符组成,求修改一段最短的连续子串使得ACGT各占整个字符串的四分之一。

50w的长度很明显要用O(n^2)以下的复杂度,我的想法是O(n)的如下:

字符串长度为len,单个字符的个数限制为limit=len/4

需要修改的最短连续字符串要么是在最左端 要么是右端  要么是中间

1、首先先从右端遍历字符串,一次把每位的字符加入统计,每种字符串的数量必须小于4,一直读到满足限制条件为止,这时已经判断了需要修改的子串在左端的情况

2、然后继续从左端开始遍历字符串,同样把美味的字符加入统计判断是否满足限制条件,记录两端的位置。

3、接下来是重点:让左端前进一个字符的方法是让右端吐出来一个和左端字符相同的字符,因此让右端一直回退到和左端将要吃进去字符相同,并把这个字符也吐出来,然后左端一直前进到满足限制条件,连续做回退直到右端完全退出来,这样就判断了子串在中间和右端的情况

每次处理出一组情况就比较一下要处理的子串长度,最后的结果就是最短子串长度

代码写的比较丑,边界处理的有些问题再见

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
int len;
int maxv;
char s[505000];
int cnt[27]={0};
int leftv;
int rightv;
int ans;
bool judge()
{
    if(cnt['A'-'A']<=maxv&&cnt['C'-'A']<=maxv&&cnt['G'-'A']<=maxv&&cnt['T'-'A']<=maxv){
        return true;
    }
    return false;
}
int main()
{
    scanf("%d",&len);
    cin>>s;
    maxv=len/4;
    ans=len;
    leftv=0;
    rightv=len-1;
    while(1){
        if(rightv>=0){
            cnt[s[rightv]-'A']++;
        }
        else{
            break;
        }
        if(judge()==true){
            rightv--;
        }
        else{
            cnt[s[rightv]-'A']--;
            rightv++;
            //cout<<rightv<<endl;
            break;
        }
    }
    rightv=max(0,rightv);
    //cout<<cnt[0]<<" "<<cnt[2]<<" "<<cnt['G'-'A']<<" "<<cnt['T'-'A']<<" "<<endl;
    int lmax=len-rightv;
    int l=lmax;
    //cout<<lmax<<endl;
    if(lmax!=0){
        while(1){
            while(1){
                cnt[s[leftv]-'A']++;
                if(judge()==true&&leftv<len){
                    l++;
                    leftv++;
                }
                else{
                    cnt[s[leftv]-'A']--;
                    break;
                }
            }
            lmax=max(l,lmax);
            //cout<<lmax<<endl;
            if(judge()==false||rightv>=len){
                break;
            }
            while(s[leftv]!=s[rightv]&&rightv<len){
                cnt[s[rightv]-'A']--;
                rightv++;
                l--;
            }
            if(rightv<len){
                cnt[s[rightv]-'A']--;
                rightv++;
                l--;
            }
        }
    }

    ans=len-lmax;
    printf("%d\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值