CF526D Om Nom and Necklace

Codeforces

Luogu

定义

$$f(S)=\left[\exists A,\exists B,S=\underbrace{A+B+A+\cdots+A+B+A}_{k+1\text{个}A,k\text{个}B}\right]$$

$\forall i\in[1,|S|]$,求 $f(pre(i))$ 的值。

分析

我们把那个谓词函数的形式转化一下,变成

$$ f(S)=\left[\exists A,\exists B\text{为}A\text{的前缀},S=\underbrace{A+A+\cdots+A+A+B}_{k\text{个}A}\right] $$

定义

$$ g(S)=\left[\exists A,S=\underbrace{A+A+\cdots+A+A}_{k\text{个}A}\right] $$

我们可以先找到所有 $g(pre(i))=1$ 的 $i$,再往后扩展。

枚举 $i=|A|$,那么 $g(pre(ik))=1$ 的充要条件是 $S[1..ik-i]=S[i+1..ik]$。

因此我们可以将 $suf(i+1)$ 和 $S$ 匹配,如果匹配长度 $\geq i(k-1)$ 说明合法。

这个匹配的实质是后缀与前缀匹配,因此可以使用 Z-Algorithm,匹配长度即 $z(i+1)$。

现在考虑往后扩展。能扩展到 $j$ 的条件是 $S[ik+1..j]$ 是 $A$ 的前缀,即是 $S$ 的一个长度 $\leq i$ 的前缀。因此扩展长度就是 $\min\left\{i,z(ik+1)\right\}$。

这时我们需要把 $[ik,ik+\min\left\{i,z(ik+1)\right\}]$ 内的答案设为 $1$,差分即可。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=1000000+10;

int n,k; char s[N]; int z[N],d[N];

int main() {
    n=read(),k=read(); scanf("%s",s+1);
    for (int i=2,mr=1,mid;i<=n;++i) {
        z[i]=i<mr?min(z[i-mid+1],mr-i):0;
        while (s[1+z[i]]==s[i+z[i]]) ++z[i];
        if (i+z[i]>mr) mr=i+z[i],mid=i;
    }
    z[1]=n;
    for (re int i=1;i*k<=n;++i)
        if (z[1+i]>=i*(k-1))
            ++d[i*k],--d[i*k+min(z[i*k+1],i)+1];
    for (re int i=1;i<=n;++i) d[i]+=d[i-1];
    for (re int i=1;i<=n;++i) putchar(d[i]?'1':'0');
    return 0;
}
最后修改:2019 年 11 月 06 日 09 : 33 AM

发表评论