But my words, like sile|

MessageBoxA

园龄:4年10个月粉丝:4关注:0

2024-09-12 00:22阅读: 13评论: 0推荐: 1

AGC007F Shik and Copying String

涉及知识点:Ad-hoc,贪心

题意

Link

给出两个长度相同的字符串 S,T,定义一次操作为:

从头至尾处理每一位,每位可以变成上一位,或者不变。

求最少对 S 进行多少次操作使得 S=T

思路

引理

可以发现,一次操作其实类似于选择一些点 l,从左到右覆盖它后面的点,并且如果记每个 li 覆盖的区间为 [li,ri],满足这些区间无交集。

因此我们可以想到,将 T 中按相邻相同的字符划分成一些段,称这段为 [li,ri] 并且段内所有字符都为 Tli,显然让这些字符由离 li 最近的满足 Sj=Tli,jliSj 覆盖而来是最优的,下文我们把这样的 j 称为该段的 pre,另外下文有时用某个 pre 来指代覆盖某段的操作

初始思路

于是我们想到了一个做法:先将 T 分段,然后倒序遍历 T 上每个段,对于每个段找到既小于该段段首也小于前一段 pre 的该段 pre。如果找到最后发现有些段找不到合法 pre 了则说明无解。至于如何计算最少操作次数,可以记录下每段 [prei,ri] 对应在 S 上存在的全局 pre 数量;此外如果 prei<li,那么说明它不能和左边的操作共存于一轮,还要 +1,取个 max 作为答案。

这样是错误的。

改进思路

我们仔细思考刚才的思路有什么不妥之处:

  1. pre 视为静态。我们统计的时候将每段的 pre 都看成了静态的,但是实际上在一轮操作中,Sprei 可以通过操作而覆盖到 prei+1 的前面(当然同时得保证 preili),由于这么做不会覆盖到其他 pre,因此不会影响合法性,但这样这个 prei 就会离 li 更近,这样一定是更优的。

    Example:

    考虑 S= abxxcdxxxT= aaabbbbcd,根据我们之前的计算方法,答案应该为 4,即 dcba 依次覆盖。但我们发现实际上不用这么做,在第一轮操作时就可以同时操作 db 得到 abbbcdddd,然后再同时操作 ac 得到 aaabccccd,最后操作 b 得到 aaabbbbcd,共 3 步。

  2. 漏掉仍有冲突的 pre我们只统计了与 prei 有冲突的 prej,并且想着如果让所有 prej 依次执行就不会有冲突,但是却没考虑 prej 在执行的过程中有可能会和更右边的 pre 发生冲突。

    Example:

    考虑 S= abcdefT= aabcde,每个 [prei,ri] 区间内都只有至多 2 个 pre,但很明显不只是 2 个 pre 有冲突。


我们现在求每个段变为不影响它右边操作合法性,且不与它左边其他操作冲突的最小操作数,那么答案便为这些操作数的最大值。考虑用一个队列维护当前段 [li,ri] 会产生影响的 prej (ij),从右到左逆序插入。

于是当我们逆序遍历到 i 时,处理分为两步:对 prei 右边的 pre 的处理,以及对 prei 本身的处理。

prei 右侧的处理

此时 prei 还未插入队列。

可以证明如果队头在 li 右侧且到 li 的距离大于队列中的元素个数,那么队头的操作合法性不受该段的影响,可以弹出。用代码表示即:while(!q.empty() && q.front()-(int)q.size()+1>l) q.pop();

为什么?我们想象在段首设立一个“缓冲区”(我们队列维护的就是这个缓冲区),可以通过一系列操作把对该段以及该段右侧有影响的 pre 都搬到“缓冲区”里,并且保证他们的相对顺序不变。这样一来,这些之前在该段左侧的操作全部可以合法的转移到该段缓冲区中,这保证了这些操作不与左侧其他操作冲突;另外由于相对顺序不变,那么也不影响这些右侧操作的合法性

而如果之前入队的某些 preli 右侧且到 li 的距离大于队列中的元素个数,那么这些 pre 即使不用缓冲区也可以保证上述两条性质,因此不用计入缓冲区。

为什么不用计入缓冲区?

如下图,pre[i+3] 就是这样“不用记入”的 pre,因为不管它向后拓展与否,它都不影响左边其他的 pre[i+1],pre[i+2] 进入缓冲区,但 pre[i+2] 就是个反例,如果它不动,那么 pre[i+1] 就无法进入缓冲区,进而 pre[i] 就必须无法拓展到 li 上,对左侧造成了影响。

那这样的 pre 造成了贡献吗?

pre[i+3] 是有贡献的,但是它的操作可以与加入缓冲区的 pre 同时操作,因此没有独立的影响。事实上,pre[i+3] 的贡献是由遍历到段 [li+3,ri+3] 的时候计算的,此时已经计算出了 pre[i+3] 不影响 pre[i] 时的操作数了。更多可以看完下文 “prei 本身的处理” 回来理解。

很明显,由于要保证相对顺序,上文中“一系列操作”的次数便为“缓冲区”的大小。

prei 本身的处理

此时 prei 已经插入队列。

根据定义,prei 不可能大于 li

  • prei<li 的情况:

    此时说明本段的 pre 会影响左侧的操作,因此为了满足 prei 不与左侧操作冲突,除了做完队列中的操作,还需要左侧额外覆盖一次多余的部分,如图,先覆盖蓝色再覆盖绿色:

  • prei=li 的情况:

    此时说明本段的 pre 不会影响左侧的操作,那么直接记录为“缓冲区”的大小即可。另外需特别注意如果 prei+1 不在本段内,即本段完全不影响右边的 pre,不能直接记为该段操作数为 1,还得判断该段 ST 是否相同,如果相同则操作数只需要 0

    实际上,对于 prei=liprei+1 不在本段内的情况(代码 48~50 行)不需要额外处理,因为这个情况在遍历到前一段 [li+1,ri+1] 的时候就由处理 prei+1<li+1 达到了等价的效果。

代码

本题思路比较抽象,很难保证能一遍理解,建议思路分析与代码结合食用。

#include<bits/stdc++.h>
#define mkp make_pair
using namespace std;
typedef pair<int,int> pii;
const int MAXN=3e6+5;
int n;
string s,t;
int fst[MAXN],ans=0;
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>s>>t;
    if(s==t){
        cout<<0<<endl;
        return 0;
    }
    s=" "+s;t=" "+t;
    int rside=n;
    for(int i=n;i>=1;i--){
        if(t[i]!=t[i+1]){
            fst[rside]=i+1;
            rside=i;
        }
    }
    fst[rside]=1;
    queue<int>q;
    for(int l,r=n,pre=n,lstpre=n+1;r>=1;r=l-1){
        l=fst[r];
        if(pre>=l) pre=l;
        while(pre>=1 && s[pre]!=t[l]) pre--;
        if(pre<1){cout<<-1<<endl;return 0;}
        
        while(!q.empty() && q.front()-(int)q.size()+1>l) q.pop();
        q.push(pre);
        if(pre<l) ans=max(ans,(int)q.size()+1);
        else if(pre==l){
            bool flag=false;
            for(int i=l;i<=r;i++){
                if(s[i]!=t[i]){
                    flag=true;break;
                }
            }
            if(flag) ans=max(ans,1);
            if(lstpre>r){//lstpre==r+1
                ans=max(ans,(int)flag);
            }
            else{
                ans=max(ans,(int)q.size());
            }
        }
        lstpre=pre;
    }
    cout<<ans<<endl;
    return 0;
}

本文作者:MessageBoxA

本文链接:https://www.cnblogs.com/SkyNet-PKN/p/18409285

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MessageBoxA  阅读(13)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 evening Corn Wave
  2. 2 Группа крови Кино
  3. 3 The Sound Of Silence Simon & Garfunkel
  4. 4 dB doll YUE.STEVEN
Группа крови - Кино
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.