P11361 [NOIP2024] 编辑字符串
算法
贪心, 模拟.
思路
特殊性质 A
因为 \(s_1\) 字符串内的字符都相同, 所以无论 \(s_2\) 中怎么排, 最大的匹配数都是不变的.
特殊性质 B
\(t_1 = t_2\), 也就是两字符串能够交换的位置相同, 而每一段只能在内部交换, 也就是每一个 \(t_i=0\) 相当于一个节点, 每两个节点中可以任意交换, 这样特殊性质 B 就好做了.
考虑贪心, 答案即为每一段中 0 的数量的最小值加上 1 的数量的最小值.
根据特殊性质 B, 我们可以知道原字符串可以当过多个字符串拼接起来, 每一个 \(t_i=0\) 便是一个节点.
从左往右扫过去, 每个位置我们可以分三种情况进行讨论:
-
- 该位置两字符串都可以交换.
因为当前不能确定这该段字符串 0 和 1 的数量, 所以我们可以先将其存下, 放在结尾再进行操作.
- 该位置两字符串都可以交换.
-
- 该位置两字符串都不能进行交换.
同特殊性质 B, 我们直接将答案加上 0 的数量的最小值加上 1 的数量的最小值即可, 最后记得清零.
- 该位置两字符串都不能进行交换.
-
- 该位置一个可换, 一个不可换.
我们进行贪心.
对于不可换的一个字符串, 这就相当于是一个节点了, 所以之前统计的 0 1 数量需要清零.
对于可换的字符串, 我们可以优先将其对这一位进行匹配了, 因为你匹配了这一位一定是不劣的.
- 该位置一个可换, 一个不可换.
#include "iostream"
#include "string"
using namespace std;
int n;
string s1, s2, f1, f2;
void init()
{
cin >> n;
cin >> s1 >> s2;
cin >> f1 >> f2;
return;
}
void calculate()
{
int idx = 0, i = 0, j = 0, ans = 0;
int cnt1[2], cnt2[2];
cnt1[0] = cnt1[1] = cnt2[0] = cnt2[1] = 0;
while (idx ^ n)
{
while (f1[i] == '1' and i ^ n)
++cnt1[s1[i] - '0'], ++i;
while (f2[j] == '1' and j ^ n)
++cnt2[s2[j] - '0'], ++j;
while (idx ^ i and idx ^ j)
{
if (cnt1[0] and cnt2[0])
{
--cnt1[0], --cnt2[0];
++ans;
}
else if (cnt1[1] and cnt2[1])
{
--cnt1[1], --cnt2[1];
++ans;
}
++idx;
}
if (idx == n)
break;
if (i == j)
{
ans += (s1[i] == s2[j]);
cnt1[0] = cnt1[1] = cnt2[0] = cnt2[1] = 0;
++i, ++j;
}
else if (i > j)
{
if (cnt1[s2[j] - '0'])
{
--cnt1[s2[j] - '0'];
++ans;
}
cnt2[0] = cnt2[1] = 0;
++j;
}
else
{
if (cnt2[s1[i] - '0'])
{
--cnt2[s1[i] - '0'];
++ans;
}
cnt1[0] = cnt1[1] = 0;
++i;
}
++idx;
}
cout << ans << '\n';
return;
}
void solve()
{
init();
calculate();
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}

浙公网安备 33010602011771号