P1053 · OFFICIAL SOLUTION

P1053 编辑字符串 官方题解

Gioush OJ · P1053 编辑字符串

【数据点 1∼41\sim 41∼4】

  • 这一档保证 n10n\leq 10。先找出所有满足 sitis_i\neq t_i 的位置,并用二进制状态记录哪些位置已经被修改。
  • fp,Sf_{p,S} 表示光标位于位置 pp,已经完成集合 SS 中的修改时,至少需要多少次移动。
  • 每次向顺时针或逆时针移动一格,并把新位置对应的修改加入状态。对状态图进行最短路即可。
  • 修改次数本身是固定的,只需要在最少移动次数上加上失配位置数量。时间复杂度为 O(n2n)\mathcal{O}(n2^n)

【数据点 5∼85\sim 85∼8】

  • 这一档保证失配位置不超过两个。只有一个失配位置时,沿环上较短的一侧到达它即可。
  • 有两个失配位置时,可以枚举先到达哪一个位置,以及到达第一个位置后是否原路返回,再取四种路线中的最小值。
  • 这个过程提示我们,一条最优路线只需要记录顺时针走到的最远位置与逆时针走到的最远位置。

【数据点 9∼129\sim 129∼12】

  • 这一档的失配位置在环上构成一个连续区间。把位置 11 作为切口后,这个区间至多被分成左右两段。
  • 若顺时针最远走 rr 步,逆时针最远走 ll 步,覆盖两段的最少移动次数为
min(2r+l,r+2l). \min(2r+l,r+2l).
  • 较短的那一侧需要走一个来回,另一侧只走一次。枚举区间的断开方式即可。

【数据点 13∼1613\sim 1613∼16】

  • 对一般的失配集合,光标走过的顺时针部分与逆时针部分仍然分别是一个连续前缀。
  • 可以枚举两个前缀的长度,再用失配位置的前缀和判断它们是否已经覆盖全部位置。
  • 每对前缀都使用 min(2r+l,r+2l)\min(2r+l,r+2l) 计算移动次数。时间复杂度为 O(n2)\mathcal{O}(n^2)
  • 当前瓶颈在于枚举了大量没有失配位置作为端点的前缀。

【正解】

设失配位置为 p1<p2<<pmp_1<p_2<\cdots<p_m,并记 di=pi1d_i=p_i-1

  • 枚举 0im0\leq i\leq m,让 p1,,pip_1,\ldots,p_i 从顺时针一侧覆盖,其余位置从逆时针一侧覆盖。
  • 两侧最远距离分别为 did_indi+1n-d_{i+1}。边界处约定 d0=0d_0=0dm+1=nd_{m+1}=n
  • 当前划分的移动次数为
min(2di+ndi+1,di+2(ndi+1)). \min\bigl(2d_i+n-d_{i+1},d_i+2(n-d_{i+1})\bigr).
  • 在所有划分中取最小值,再加上固定的 mm 次修改。时间复杂度为 O(n)\mathcal{O}(n)

【正确性说明】

  • 任意一条路线在顺时针与逆时针方向走过的位置都各自构成一个前缀,所以一定对应某个划分 ii
  • 固定两个前缀以后,光标最后停在其中一侧。另一侧必须走一个来回,因此移动次数至少为 min(2r+l,r+2l)\min(2r+l,r+2l)
  • 先完整走较短侧并返回,再走另一侧,恰好达到这个下界。因此枚举已经覆盖所有最优路线。

【参考代码】

/*Author:EhundateghDate:2026/8/2Name:edit.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 100010using namespace std;int c,T,n,Cnt,Pos[MAXN];char s[MAXN],t[MAXN];void Solve(){    scanf("%d%s%s",&n,s+1,t+1);    Cnt=0;    for(int i=1;i<=n;i++){        if(s[i]!=t[i]) Pos[++Cnt]=i-1;    }    if(!Cnt){printf("0\n");return;}    int Move=n;    for(int i=0;i<=Cnt;i++){        int Clockwise=i?Pos[i]:0;        int Counter=i==Cnt?0:n-Pos[i+1];        Move=min(Move,min(Clockwise*2+Counter,Clockwise+Counter*2));    }    printf("%d\n",Cnt+Move);    return;}int main(){    scanf("%d%d",&c,&T);    while(T-->0) Solve();    return 0;}