【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\n",step);
    for(int i=1;i<=step;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}
上一篇:M - Help Hanzo LightOJ - 1197 (大区间素数筛法)


下一篇:数据分析案例_电子游戏的销售情况