码迷,mamicode.com
首页 > 其他好文 > 详细

【Luogu】 P6303 [eJOI2018]AB 串 题解

时间:2020-06-03 13:27:30      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:cpp   scan   strong   相同   mat   code   直接   mes   name   

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

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

什么意思呢?比如,对于串 \(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;
}

【Luogu】 P6303 [eJOI2018]AB 串 题解

标签:cpp   scan   strong   相同   mat   code   直接   mes   name   

原文地址:https://www.cnblogs.com/acceptedzhs/p/13036840.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!