CF Round #658 (Div. 2) C2. Prefix Flip (Hard Version)

题目

题目链接

思路

2n的操作是,对b数组从后往前遍历,如果当前位置与a匹配,继续下一个,如果不匹配,看a第一个元素是否匹配,如果不相同,直接对当前位置的前缀做操作,如果相同,则对a的第一个元素做一次操作,然后再对当前位置的前缀做操作。这样能保证在2n内完成。

不过t<=1e3 n <=1e5,对执行操作的区间进行线性更新会TLE。
所以找一个更快的维护方式。
记录当前区间L R,flag标记当前方向是正还是反。
从后往前跑一遍b数组,不断更新L R,记录信息即可。

这里代码写麻烦了,实际可以记录对区间操作了多少次,然后根据次数的奇偶性知道某个区间上的元素是否与原来相同,然后每次只需要直接更新,并交换L R即可,而不用讨论方向的正反。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1e5 + 10;
char s1[maxn], s2[maxn];
int main(){
    int t; scanf("%d", &t);
    while(t--){
        queue<int>que;
        int n; scanf("%d", &n);
        scanf("%s", s1 + 1);
        scanf("%s", s2 + 1);
        int cnt = 0, times = 0;//取反了多少次 区间取反个数
        bool flag = true;   //true表示正,从右到左
        int L = 1, R = n;
        for(int i = n; i >= 1; i--){
            if(flag){//从右到左
                if(s2[i] == s1[R]){//当前位置与s1串匹配
                    R--;
                    continue;
                }
                if(s2[i] == s1[L]){//当前位置与头位置的相同
                    que.push(1);
                }
                que.push(i);
                L++;
                flag ^= 1;
            }else{//从左到右
                if(s2[i] != s1[L]){//当前位置与s1串匹配
                    L++;
                    continue;
                }
                if(s2[i] != s1[R]){//当前位置与头位置的相同
                    que.push(1);
                }
                que.push(i);
                R--;
                flag ^= 1;
            }
        }
        printf("%d ", que.size());
        while(!que.empty()){
            int x = que.front();
            que.pop();
            printf("%d ", x);
        }
        puts("");
    }
	return 0;
}

另一种码法

CF Round #658 (Div. 2) C2. Prefix Flip (Hard Version)_第1张图片

你可能感兴趣的:(思维)