1 条题解

  • 0
    @ 2025-8-24 22:59:58

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Nangu
    加 训

    搬运于2025-08-24 22:59:58,当前版本为作者最后更新于2024-08-15 13:49:59,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题意

    有两个由 +- 组成的字符串 SSTT,你需要进行若干次操作,每次可以将 SS 中一段极长串翻转,满足这个极长串的长度小于其任意一个相邻的极长串的长度。问 SS 是否能经过变换得到 TT。多组数据。

    题解

    我们从特殊性质开始考虑:TT 中没有 -

    对于 SS 中任意一个极长的、由 - 构成的串,我们必须想办法将其翻转。

    首先有一个接近 O(n2)O(n^2) 的暴力:将所有的极长串用链表连接,每次枚举一段由 + 组成的串,判断其右边的串是否能翻转。若能,则将这段串右边、右边的右边的串都删掉,并增加这段串的长度。最后,若不能操作时仍有 -,则输出 No,否则输出 Yes,代码如下:

    
    void link(int u,int v){
        nxt[u]=v, pre[v]=u;
    }
    
    bool check(){
        s[n+1]=t[n+1]='\0';
        cnt=0;
        if(s[1]=='-') cnt=1, siz[1]=0;
        int tmp=1;
        rep(i, 2, n+1){
            if(s[i]!=s[i-1]) siz[++cnt]=tmp, tmp=1;
            else ++tmp;
        }
        if(cnt%2==0) siz[++cnt]=0;
        rep(i, 1, cnt-1) link(i, i+1);
        nxt[cnt]=0;
        while(nxt[1]){
            bool flag=0;
            for(int i=1, j; nxt[i];){
                j=nxt[nxt[i]];
                if(siz[j]>siz[nxt[i]] || siz[i]>siz[nxt[i]]){
                    siz[i]+=siz[j]+siz[nxt[i]];
                    link(i, nxt[j]);
                    flag=1;
                }
                i=j;
            }
            if(!flag) break;
        }
        return !nxt[1];
    }
    

    这段代码在随机数据下的复杂度应该是 O(n)O(n),仅当 SS 存在类似 --+--+--+-++ 的串时,复杂度退化为 O(n2)O(n^2)。因此考虑添加一个优化:当一次翻转成功时,我们回退到上一个极长 + 串,再进行判断。这样就变成 O(n)O(n) 了。只要改动for循环中的一部分内容就好了:

                j=nxt[nxt[i]];
                if(siz[j]>siz[nxt[i]] || siz[i]>siz[nxt[i]]){
                    siz[i]+=siz[j]+siz[nxt[i]];
                    link(i, nxt[j]);
                    flag=1;
                    if(pre[i]) i=pre[pre[i]];
                } else i=j;
    

    接下来正解就水到渠成啦!!对于 TT 的每一个极长段,我们可以对其分开讨论,这样就变成了若干个特殊性质的叠加。

    需要注意的一点是,若对于 TT 的某一个极长串,ss 在对应的位置的两边并不属于一个独立的极长串,就无法操作了。此时直接输出 No 就好。

    于是,这道题就做完了。

    Code

    const int N=1e6+7;
    int n;
    char s[N], t[N];
    
    namespace sub{
    int n;
    char s[N], t[N];
    int siz[N], pre[N], nxt[N], cnt;
    
    void link(int u,int v){
        nxt[u]=v, pre[v]=u;
    }
    
    bool check(){
        s[n+1]=t[n+1]='\0';
        // cout<<s+1<<' '<<t+1<<endl;
        if(t[1]=='-'){
            rep(i, 1, n)
                s[i]=s[i]=='-'?'+':'-';
        }
        cnt=0;
        if(s[1]=='-') cnt=1, siz[1]=0;
        int tmp=1;
        rep(i, 2, n+1){
            if(s[i]!=s[i-1]) siz[++cnt]=tmp, tmp=1;
            else ++tmp;
        }
        if(cnt%2==0) siz[++cnt]=0;
        rep(i, 1, cnt-1) link(i, i+1);
        nxt[cnt]=0;
        while(nxt[1]){
            bool flag=0;
            for(int i=1, j; nxt[i];){
                j=nxt[nxt[i]];
                if(siz[j]>siz[nxt[i]] || siz[i]>siz[nxt[i]]){
                    siz[i]+=siz[j]+siz[nxt[i]];
                    link(i, nxt[j]);
                    flag=1;
                    if(pre[i]) i=pre[pre[i]];
                } else i=j;
            }
            if(!flag) break;
        }
        return !nxt[1];
    }
    }
    
    void run(){
        cin>>s+1>>t+1;
        n=strlen(s+1);
        for(int i=1; i<=n;){
            int pos=i;
            while(pos<=n && t[pos]==t[i]) ++pos;
            if(i!=1 && s[i]==s[i-1] || pos!=n+1 && s[pos]==s[pos-1]){
                cout<<"No\n";
                return;
            }
            sub::n=pos-i;
            rep(j, 1, pos-i)   
                sub::s[j]=s[i+j-1], sub::t[j]=t[i+j-1];
            if(!sub::check()){
                cout<<"No\n";
                return;
            }
            rep(j, i, pos-1) s[j]=t[j];
            i=pos;
        }
        cout<<"Yes\n";
    }
    
    signed main(){
        ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
        int t; cin>>t;
        while(t--) run();
    }
    

    都看到这里了,点个赞再走呗。

    • 1

    信息

    ID
    10444
    时间
    2000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者