[luoguP11361/NOIP2024] 编辑字符串

题意

给出两个 0/1 字符串,每个字符串有一些位置被标记,无法交换。求通过任意多次的交换相邻元素操作能够使两个字符串最多多少位置相同。

sol

一道贪心题。
显然交换相邻的操作可以使该字符串可以交换的一段任意排列。由于不同位置的贡献最大只为 1,因此在任何位置贡献都没有区别,考虑将贡献的位置尽可能向左靠拢。将两个字符串按照无法交换的位置分段,当遇到某两段的分界点时,如果是两个字符串的分界点则计算分界点左侧能够匹配的数量,公式为 min(min(cnt1,0,cnt2,0),min(cnt1,1,cnt2,1));如果是一个字符串的分界点,则使用另一侧的分界点尽可能地匹配,注意最右端的边界也需要作为分界点处理。

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 100005;

char s1[N], s2[N], t1[N], t2[N];
int T, n;
int cnt10[N], cnt11[N], rb1[N];
int cnt20[N], cnt21[N], rb2[N];

int main(){
    // freopen("edit2.in", "r", stdin);
    scanf("%d", &T);
    while (T -- ){
        scanf("%d%s%s%s%s", &n, s1 + 1, s2 + 1, t1 + 1, t2 + 1);
        for (int i = 1; i <= n; i ++ ) {
            cnt10[i] = cnt10[i - 1], cnt11[i] = cnt11[i - 1];
            cnt20[i] = cnt20[i - 1], cnt21[i] = cnt21[i - 1];
            if (s1[i] == '1') cnt11[i] ++ ;
            else cnt10[i] ++ ;
            if (s2[i] == '1') cnt21[i] ++ ;
            else cnt20[i] ++ ;
        }
        rb1[n + 1] = rb2[n + 1] = n;
        for (int i = n; i; i -- ){
            if (t1[i] == '1') rb1[i] = rb1[i + 1];
            else rb1[i] = i - 1;
            if (t2[i] == '1') rb2[i] = rb2[i + 1];
            else rb2[i] = i - 1;
        }

        int left0 = 0, left1 = 0, state = -1, ans = 0;
        int last = 0;
        for (int i = 1; i <= n + 1; i ++ ) {
            if (i <= n && t1[i] == '1' && t2[i] == '1') continue;
            if (t1[i] == '1') {
                int need0 = cnt20[i - 1] - cnt20[last], need1 = cnt21[i - 1] - cnt21[last];
                if (state == 2) need0 = min(left0, i - last - 1), need1 = min(left1, i - last - 1);
                if (state != 1) left0 = cnt10[rb1[i]] - cnt10[last], left1 = cnt11[rb1[i]] - cnt11[last];
                int prov0 = min(need0, left0), prov1 = min(need1, left1);
                ans += prov0 + prov1;
                left0 -= prov0, left1 -= prov1;
                if (s2[i] == '0' && left0) ans ++ , left0 -- ;
                if (s2[i] == '1' && left1) ans ++ , left1 -- ;
                state = 1;
            }
            else if (t2[i] == '1') {
                int need0 = cnt10[i - 1] - cnt10[last], need1 = cnt11[i - 1] - cnt11[last];
                if (state == 1) need0 = min(left0, i - last - 1), need1 = min(left1, i - last - 1);
                if (state != 2) left0 = cnt20[rb2[i]] - cnt20[last], left1 = cnt21[rb2[i]] - cnt21[last];
                int prov0 = min(need0, left0), prov1 = min(need1, left1);
                ans += prov0 + prov1;
                left0 -= prov0, left1 -= prov1;
                if (s1[i] == '0' && left0) ans ++ , left0 -- ;
                if (s1[i] == '1' && left1) ans ++ , left1 -- ;
                state = 2;
            }
            else {
                int need10 = cnt10[i - 1] - cnt10[last], need11 = cnt11[i - 1] - cnt11[last];
                int need20 = cnt20[i - 1] - cnt20[last], need21 = cnt21[i - 1] - cnt21[last];
                if (state == 1) need10 = min(left0, i - last - 1), need11 = min(left1, i - last - 1);
                if (state == 2) need20 = min(left0, i - last - 1), need21 = min(left1, i - last - 1);
                ans += min(need10, need20) + min(need11, need21);
                if (s1[i] == s2[i]) ans ++ ;
                left0 = left1 = 0;
                state = -1;
            }
            last = i;
        }
        printf("%d\n", ans - 1);
    }

    return 0;
}
posted @   是一只小蒟蒻呀  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示