C2.Prefix Flip (Hard Version)
题意
定义一种操作:
给出一个 01 串 s,现在可以选择一个前缀,将 01 翻转,并且将整个串倒过来。
现在给出两个串 s,t,问最少需要多少次操作使得 s = t。
思路
从后依次向前,依次使得(s_i = t_i)
当前 (s_i != t_j) :
(s_0 != t_j) :
对前缀 i 执行依次操作,现在 (s_i = s_j)
(s_0 = t_j):
先对前缀 1 执行一次操作使得(s_0 != t_j)
再按照上一步执行
思路就是这样,但是发现长度太大,不可能真的去 01 变换,进行翻转。
对于当前 i 我们无非就是要知道,现在到底是谁在这个位置?而且这个位置 01 变换了几次?
关于 01变换:
假如在下标 i 之前有 num 个位置进行了变换,那么下标 i 的变换次数最少为 num 次。要加上前缀 1 的变换对当前位置的影响。
关于翻转:
如果现在长度为 6,分别在 6、5、4、3位置发生了变换,不考虑前缀 1 的变换。
假如此时只是 6 位置发生了翻转,那么第 i 个位置代表的真正的字符是 (s[7 - i])
此时 6、5位置发生了翻转,那么第 i 个位置代表的真正的字符是 (s[7-6+i])
此时 6、5、4位置发生了翻转,那么第 i 个位置代表的真正的字符是 (s[7-6+5-i])
此时6、5、4、3的位置发生了翻转,那么第 i 个位置代表的真正的字符是 (s[7-6+5-4+i])
现在就可以发现规律了。
我们定义 (op[i]) 表示 s 串第 i 个位置额外翻转的次数,使用real(i)表示下标 i 表示的字符在 s 中的下标,那么 num + op[real(i)] 就是这个位置翻转的次数,再根据 (s[real(i)]) 就可以知道当前的字符是什么了。
在根据上面的步骤运算就可以了。
代码
/*
* @Autor: valk
* @Date: 2020-07-17 16:50:40
* @LastEditTime: 2020-08-17 17:02:13
* @Description: 如果邪恶 是 华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
*/
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
char s[N], t[N];
int op[N], pre, num;
vector<int> ans;
char get(int real)
{
int time = num + op[real];//这个位置的字符变换次数
char c = s[real];
if (time % 2) {
c = (c == '1') ? '0' : '1';
}
return c;
}
int main()
{
int _;
scanf("%d", &_);
while (_--) {
memset(op, 0, sizeof(op));
ans.clear();
int n;
scanf("%d%s%s", &n, s + 1, t + 1);
pre = num = 0;
for (int i = n; i > 1; i--) {
int real = num % 2 ? pre - i : pre + i;// 第 i 个位置的在 s 中的位置
char c = get(real);//第 i 个位置的字符
if (c == t[i])//不需要变换
continue;
real = num % 2 ? pre - 1 : pre + 1;//第 1 个字符的位置
c = get(real);//第 1 个位置的字符
if (c == t[i]) {//相等要额外对前缀 1 进行一次变换
ans.pb(1);
op[real]++;//该位置操作次数增加
}
ans.pb(i);//不相等直接变换
if (num % 2)
pre -= i + 1;
else
pre += i + 1;
num++;
}
int real = num % 2 ? pre - 1 : pre + 1;
char c = get(real);
if (c != t[1]) {
ans.pb(1);
num++;
}
printf("%d ", ans.size());
for (int v : ans) {
printf("%d ", v);
}
printf("
");
}
return 0;
}