关于本题的一种贪心做法的正确性
查看原帖
关于本题的一种贪心做法的正确性
67942
meyiMessiah楼主2021/6/19 23:21

注:作者并不熟悉字符串相关知识,所以不知道下面的证明的表述是否严谨,是否为伪证,请各位大佬帮忙看下其正确性/kk

以下将字符串 ss 的前缀 s[1..n]s[1..n] 称为 prefix(n)\text{prefix}(n),将字符串 ss 无限连接自身构成的字符串的前 kk 个字符称为 fk(s)f_k(s),即 fk(s)={s[1..k]  (sk)fk(s+s)  (s<k)f_k(s)=\left\{ \begin{array}{l} s[1..k]\ \ (|s|\ge k) \\ f_k(s+s)\ \ (|s|<k) \end{array} \right.

将题意转化为求 fk(prefix(x))f_k(prefix(x)) 最小的 xx,有一种贪心做法如下:

枚举前缀的结尾位置 ii ,设当前找到的最优前缀为 posposfk(prefix(pos))[i]f_k(prefix(pos))[i]tt,比较 s[i]s[i]tt 的大小关系,有如下三种情况:

  1. s[i]>ts[i]>t,此时由于 ii 以后的每个位置的前缀都会包含 s[i]s[i],所以 ii 以后不会有更优解,因此直接退出循环。

  2. s[i]<ts[i]<t,此时若有 s[1..i1]=fk(prefix(pos))[1..i1]s[1..i-1]=f_k(\text{prefix}(pos))[1..i-1] ,则此时 ii 优于 pospos,更新答案。反证一下满足条件,按上述方式循环,若 s[1..i1]>fk(prefix(pos))[1..i1]s[1..i-1]> f_k(\text{prefix}(pos))[1..i-1],则在 i1i-1 或更早的位置就已退出循环;若 s[1..i1]<fk(prefix(pos))[1..i1]s[1..i-1]<f_k(\text{prefix}(pos))[1..i-1],则不符合 pospos 是最优解的定义。故 s[1..i1]=fk(prefix(pos))[1..i1]s[1..i-1]=f_k(\text{prefix}(pos))[1..i-1],满足更新答案的条件。

  3. s[i]=ts[i]=t,此时直接继续枚举 i+1i+1。@九九九九 大佬的题解中提出该做法有被 hack 的可能性,但 fk(prefix(i))[i+1]=s[1]f_k(prefix(i))[i+1]=s[1]fk(prefix(pos))[i+1]=s[(imodpos)+1]f_k(prefix(pos))[i+1]=s[(i\mod pos)+1],不难得到 s[(imodpos)+1]s[1]s[(i\mod pos)+1]\le s[1],因为若 s[(imodpos)+1]>s[1]s[(i\mod pos)+1]>s[1],则我们直接在此处填 s[1]s[1] 会更优,不符合 pospos 是最优解的定义,同理 fk(prefix(i))f_k(prefix(i))fk(prefix(pos))f_k(prefix(pos)) 的第 i+2i+2i+3i+3\cdotskk 个字符也可按上述方式比较大小关系。因此 ii 不会比 pospos 更优,但继续枚举下去可能会有更优解。

相关代码:

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define x (i-1)%pos+1
const int maxn=5e5+10;
int m,n;
char t[maxn];
int main(){
    scanf("%d%d%s",&n,&m,t+1);
    ri k=min(m,n),pos=1;
    for(ri i=2;i<=k;++i)
        if(t[i]<t[x])pos=i;
        else if(t[i]>t[x])break;
    for(ri i=1;i<=m;++i)putchar(t[x]);
    return 0;
}

(如果有显然的错误就当我是**

2021/6/19 23:21
加载中...