合并字符串的价值------线段树+暴力

原题大佬
所谓的将a,b两个字符串合并之后取分界点,然后将左右两边排序求最大前缀和的长度。可以简化看作,该分割法可以使得左右两边的相同字母的个数最多。
所以答案一定是ans=f(A)+f©+f(G)+f(T),其中f(x)=min(L(x),R(x))。
然后该考虑是怎么分割,其实就是只有3中分法:
1:从a中分一条线,aL是左,aR+b是右。
2:从b中分一条线,bL是左,a+bR是右。
3:从a和b中都分一条线,aL+bL是左,aR+bR是右。
所以我们可以先固定a字符串,在a字符串中做到分法1的所有可能,也就是,如果仅仅在a的最左边分出一条线的价值是org[0]=0(也就是a不分),如果在a的第一个字母后分一条线的价值是org[1],如果第二个字母后是org[2]…如果在最后一个字母后价值是org[n]。将这个org数组建一个线段树,求线段树的最大值。
然后我们再分b,从b字符串的第一个字母开始分,看一直分到第m个(b的每一次分,都要在上一次分的影响下完成)。每一次分,都要判断对a字符串的各种分法的影响。用ans来保存历史最大值。
这个分b字符串对a的分法的影响,是比较重要的

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lson k<<1,l,mid
#define rson k<<1|1,mid+1,r
#define ll long long
using namespace std;
const int MX=1e5+9;
char a[MX],b[MX];
int tot[4],pos[4][MX],num[MX],org[MX],t[MX<<2],laze[MX<<2],nw[MX];

void init(){
    memset(t,0,sizeof(t));
    memset(pos,0,sizeof(pos));
    memset(tot,0,sizeof(tot));
    memset(num,0,sizeof(num));
    memset(org,0,sizeof(org));
    memset(laze,0,sizeof(laze));
    memset(nw,0,sizeof(nw));
}

int panduan(char c){
    if( c=='A' )
        return 0;
    else if( c=='C' )
        return 1;
    else if( c=='G' )
        return 2;
    else
        return 3;
}

void change(int k,int x){
    t[k]+=x;
    laze[k]+=x;
    return ;
}

void pushup(int k){
    t[k]=max(t[k<<1],t[k<<1|1])+laze[k];
}

void build(int k,int l,int r){
    laze[k]=0;
    if( l==r ){
        t[k]=org[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(k);
}

void update(int k,int l,int r,int L,int R,int x){
    if( L<=l && r<=R ){
        change(k,x);
        return ;
    }
    int mid=(l+r)>>1;
    if( L<=mid )
        update(lson,L,R,x);
    if( mid<R )
        update(rson,L,R,x);
    pushup(k);
}

int main()
{
  //  freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while( T-- ){
        init();
        scanf("%s %s",a+1,b+1);
        int n=strlen(a+1),m=strlen(b+1);
        for( int i=1 ; i<=n ; i++ )
            tot[panduan(a[i])]++;
        for( int i=1 ; i<=m ; i++ )
            tot[panduan(b[i])]++;
        for( int i=1 ; i<=n ; i++ ){
            int c=panduan(a[i]);
            pos[c][++num[c]]=i;// pos存第x的的c的位置为pos[c][x]
            for( int j=0 ; j<=3 ; j++ )
                org[i]+=min(num[j],tot[j]-num[j]);// org存如果仅仅分割左边的字符串的价值
        }
        build(1,0,n);
        int ans=0;
        for( int i=1 ; i<=m ; i++ ){
            ans=max(ans,t[1]);
            int c=panduan(b[i]);
            nw[c]++;
            int p=tot[c]/2-nw[c]+1;
            if( p>0 )// 如果相比于一半有余下的,说明左边有机会+1
                update(1,0,n,0,p<=num[c]?pos[c][p]-1:n,1);// 只有p的前面才有机会+1
            p=(tot[c]+1)/2-nw[c]+1;
            if( p<=num[c] ) // 如果余下的小于左边的总数量,那么左边就有机会-1
                update(1,0,n,p>0?pos[c][p]:0,n,-1);// 只有p的后面才会-1
        }
        printf("%d\n",max(ans,t[1]));
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值