AT2173 Shik and Copying String [思维题]

S h i k   a n d   C o p y i n g   S t r i n g Shik\ and\ Copying\ String Shik and Copying String

题目描述请点击题目链接 .


最 初 想 法 \color{blue}{最初想法}

先检查 无解情况 骗点分 .
因为只有前面才会对后面造成影响, 所以 从后向前处理,
分情况讨论

  1. S i = T i S_i=T_i Si=Ti, 无覆盖, 跳过 .
  2. S i = T i S_i=T_i Si=Ti, 有相应覆盖, 跳过 .
  3. S i = T i S_i=T_i Si=Ti, 无相应覆盖, 查找 ( 1 , M i n _ L ) (1, Min\_L) (1,Min_L) S t = T i S_t=T_i St=Ti t t t, 从 t t t 向后覆盖, 若找不到, 无解. 若有其他覆盖, A n s + + Ans ++ Ans++ .
  4. S i ≠ T i S_i \neq T_i Si̸=Ti, 有相应覆盖, 跳过 .
  5. S i ≠ T i S_i \neq T_i Si̸=Ti, 无相应覆盖, 同 3. 3. 3.

对影响的消除部分有细节 .
栈不要开 1 e 6   ! ! ! 1e6\ !!! 1e6 !!!, 会 M L E MLE MLE .


正 解 部 分 \color{red}{正解部分}

在这里插入图片描述

大体思路跟上方差不多, 我怎么就没想到画二维图?!

将过程图画出来, 发现字母之间覆盖关系马上就清楚了许多 .

  1. 字母覆盖折线不能相交.
  2. 覆盖折线要尽量 " 贴 " "贴" "" 着走 .

考虑一个折线的产生对以后的折线有什么影响,
看到图中红色箭头部分, 是 c c c 产生的折线使得 a a a 产生的折线不得不弯曲一次,

这也可以看成是 c c c 的折线拐点 传递 到了 a a a, 使得 a a a 产生第 2 2 2 个拐点 .
假设又有新的折线加入, 可能又会接着 a a a 的第二个拐点继续传, 进而产生第 3 3 3个拐点, 同时 a a a 的第一个拐点又会传递产生 新折线 的第 2 2 2 个拐点 , 以此类推 .

设 设 当前 从后往前 遍历到了 i i i, 且在 ( 1 , 最 后 的 覆 盖 起 点 ) (1, 最后的覆盖起点) (1,) 中满足 S t = T i S_t = T_i St=Ti 的最大的编号为 t t t,

前方某个折线的 覆 盖 起 点 覆盖起点 b g bg bg, 传递了 l e n len len 次,

则当且仅当 b g − l e n + 1 ≤ i bg - len + 1 \le i bglen+1i 的时候那个折线才会对现在造成影响,

于是使用队列维护从后往前的操作中产生的折线, 每次弹出无用的折线, 剩下的折线对当前 i i i 造成影响,
设队列大小为 q _ s i z e q\_size q_size, 则会多产生 q _ s i z e q\_size q_size 个拐点 , 加上 当前折线 产生的 1 1 1 个拐点, 总共有 q _ s i z e + 1 q\_size + 1 q_size+1 个拐点 .

E N D \mathcal{END} END .


实 现 部 分 \color{red}{实现部分}

使用队列维护 .
注意当 i = M i n _ l i=Min\_l i=Min_l 时折线只有一个转折点 .

#include<bits/stdc++.h>
#define reg register

const int maxn = 1000005;

int N;

char S[maxn];
char T[maxn];

int main(){
        scanf("%d", &N);
        scanf("%s%s", S+1, T+1);
        int flag = 1;
        for(reg int i = 1; i <= N; i ++)
                if(S[i] != T[i]){ flag = 0; break ; }
        if(flag){ printf("0\n"); return 0; }
        int Min_l = N, Ans = 1;
        std::queue <int> Q;
        for(reg int i = N; i >= 1; i --){
                Min_l = std::min(i, Min_l);
                if(T[i] == T[i-1]) continue ;
                while(Min_l && S[Min_l] != T[i]) Min_l --;
                if(!Min_l){ printf("-1\n"); return 0; }
                while(!Q.empty()){
                        if(Q.front()-Q.size()+1 > i) Q.pop();
                        else break ;
                }
                Q.push(Min_l);
                if(Min_l != i) Ans = std::max(Ans, (int)Q.size() + 1);
        }
        printf("%d\n", Ans);
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值