Luogu P6303 [eJOI2018]AB 串 题解

Posted acceptedzhs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P6303 [eJOI2018]AB 串 题解相关的知识,希望对你有一定的参考价值。

首先,我们可以发现一个性质:

每一次交换的前缀,必然是该串由相同字母构成的最长前缀,或者空串。

什么意思呢?比如,对于串 (aaabb) ,显然选 (aa) 没有 (aaa) 划算。


接下来,我们要分两种情况讨论:

1. 两串首字母不同

如: (aaaaabb)(bbbbbaa) ,直接选两串的由相同字母构成的最长前缀,即 (aaaaa)(bbbbb) 交换即可。

2. 两串首字母相同

如: (aaaaabb)(ababababa) 。显然,我们不能选两串的由相同字母构成的最长前缀了。我们考虑把其中一个换成空串 (要不 (rand) 一下?),此时就转化为第一种情况了。但是——

对于串 (aaaaabb)(aaaaa) ,显然我们选第二个串为空串。这种情况特判一下即可。


然而,这样如果暴力修改,显然会超时。

我们考虑把串分段,每一段中字母都相同。如: (aaaabbbbaaba) 可分成(5)段:(aaaa|bbbb|aa|b|a)

修改操作就极为容易了,把两个串的第一段交换,或者把一个串的第一段挪到另一个串开头,然后合并段即可。

如:串 (aaaabbbbaaba)(bbbaaab)

先分段:(aaaa|bbbb|aa|b|a)(bbb|aaa|b)

交换:(bbb|bbbb|aa|b|a)(aaaa|aaa|b)

合并前面的段:(bbbbbbb|aa|b|a)(aaaaaaa|b)

就这样循环,直到两串段数都为(1)(即字符都相同了)退出循环就行啦!

最后注意一下,由于要在串的开头增加元素,所以要开个双端队列。


(Code:)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,lena,lenb,ans[1000005][2];
int heada=1e6+1,taila=1e6,headb=1e6+1,tailb=1e6,step; //两个双端队列的首尾
char a[5000005],b[5000005];
struct Block{ //段
    int typ,len;
}c[2][10000005];
int main(){
    scanf("%s%s",a+1,b+1);
    lena=strlen(a+1),lenb=strlen(b+1);
    int last=-1; //分段
    for(int i=1;i<=lena;i++){
        if(last!=a[i]-‘a‘){
            c[0][++taila].typ=a[i]-‘a‘;
            c[0][taila].len=1;
            last=a[i]-‘a‘;
        }
        else c[0][taila].len++;
    }
    last=-1;
    for(int i=1;i<=lenb;i++){
        if(last!=b[i]-‘a‘){
            c[1][++tailb].typ=b[i]-‘a‘;
            c[1][tailb].len=1;
            last=b[i]-‘a‘;
        }
        else c[1][tailb].len++;
    }
    while(true){
        if(heada==taila&&headb==tailb) break; //都只有一段了,直接退出
        step++;
        if(c[0][heada].typ!=c[1][headb].typ){
            ans[step][0]=c[0][heada].len,ans[step][1]=c[1][headb].len;//记录答案
            swap(c[0][heada],c[1][headb]);
            int sum=c[0][heada].len,j; //合并
            for(j=heada+1;j<=taila;j++){
                if(c[0][j].typ==c[0][heada].typ) sum+=c[0][j].len;
                else break;
            }
            --j;c[0][heada=j].len=sum;
            sum=c[1][headb].len;
            for(j=headb+1;j<=tailb;j++){
                if(c[1][j].typ==c[1][headb].typ) sum+=c[1][j].len;
                else break;
            }
            --j;c[1][headb=j].len=sum;
            continue;
        }
        else{
            int from;
            if(heada==taila) from=1;
            else from=0; //判断把哪个串换成空串
            if(from){
                ans[step][0]=0,ans[step][1]=c[1][headb].len;
                c[0][--heada]=c[1][headb];headb++;
            }
            else{
                ans[step][0]=c[0][heada].len,ans[step][1]=0;
                c[1][--headb]=c[0][heada];heada++;
            }
            int sum=c[0][heada].len,j;
            for(j=heada+1;j<=taila;j++){
                if(c[0][j].typ==c[0][heada].typ) sum+=c[0][j].len;
                else break;
            }
            --j;c[0][heada=j].len=sum;
            sum=c[1][headb].len;
            for(j=headb+1;j<=tailb;j++){
                if(c[1][j].typ==c[1][headb].typ) sum+=c[1][j].len;
                else break;
            }
            --j;c[1][headb=j].len=sum;
            continue;
        }
    }
    printf("%d
",step);
    for(int i=1;i<=step;i++) printf("%d %d
",ans[i][0],ans[i][1]);
    return 0;
}

以上是关于Luogu P6303 [eJOI2018]AB 串 题解的主要内容,如果未能解决你的问题,请参考以下文章

题解 P5089 [eJOI2018]元素周期表

P5089 [eJOI2018]元素周期表(并查集)

EJOI2017-骆驼

Codeforces Round #500 (Div. 2) [based on EJOI]

历年各赛事真题选做

[luogu2216 HAOI2007] 理想的正方形 (2dST表 or 单调队列)