Luogu P3526 [POI2011]OKR-Periodicity

Posted cjoiershiina-mashiro

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3526 [POI2011]OKR-Periodicity相关的知识,希望对你有一定的参考价值。

Link
存在一个(m)的period(Leftrightarrow)存在一个长度为(n-m)的border。
因此对于字符串(s),我们处理出它的border集合(包含(n)),并将其升序排序。
假设(s)的border集合为(a_1,cdots,a_m)
那么我们有一个很自然的想法:
先构造出满足border集合为({a_1})的长度为(a_1)的字典序最小的01串(s_1)
然后构造出满足border集合为({a_1,a_2})的长度为(a_2)的字典序最小的01串(s_2)
。。。
构造出border集合为({a_1})的长度为(a_1)的字典序最小的01串(s_1)是很容易的:
(s_1=egin{cases}0&a_1=1\(a_1-1)*0+1&a_1>1end{cases})
然后我们考虑如何已知border集合为({a_1,cdots,a_{i-1}})的长度为(a_{i-1})的字典序最小的01串(s_{i-1})求出border集合为({a_1,cdots,a_i})的长度为(a_i)的字典序最小的01串(s_i)
让我们分几种情况讨论:
(1.2a_{i-1}ge a_i)
我们只需要将(s_{i-1})的后(a_i-a_{i-1})位复制并接在(s_{i-1})的后面即可得到(s_i)
(2.2a_{i-1}<a_i)
首先(s_{i-1})一定会在(a_i)的头尾出现两次。
然后我们有一个很简单的想法是中间填(a_i-2a_{i-1})0
但是这样可能会多出来一些其它的border。
因此先让(s_i=s_{i-1}+(a_i-2a_{i-1})*0),然后检查此时(s_i)是否存在一个长度为(a_i-a_{i-1})的约数的period。
如果存在那么我们再接一个(s_{i-1})到后面就会产生不合法的border,因此我们先把当前(s_i)的最后一个字符替换成1再把(s_{i-1})接在后面。
如果不存在那么我们直接把(s_{i-1})接在后面就行了,这样并不会产生不合法的border。
注意到(s_i=s_{i-1}+???),并且删除末尾字符的总量是(O(n))的。
因此我们预处理出(s)的next数组,然后实时维护(s_i)的next数组即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=200007;
char str[N],ans[N];
int T,n,m,p,next[N],a[N],fail[N];
void ins(int c)
{
    ans[++p]=c;
    if(p>1)
    {
    for(fail[p]=fail[p-1];fail[p]&&ans[fail[p]+1]^ans[p];fail[p]=fail[fail[p]]);
    fail[p]+=ans[fail[p]+1]==ans[p];
    }
}
int main()
{
    for(scanf("%d",&T);T;--T)
    {
    scanf("%s",str+1),n=strlen(str+1);
    for(int i=2,k=0;i<=n;next[i]=k+=str[k+1]==str[i],++i) while(k&&str[k+1]^str[i]) k=next[k];
    a[m=0]=p=0;
    for(int i=n;i;i=next[i]) a[++m]=i;
    for(int i=1;i<=m/2;++i) std::swap(a[i],a[m+1-i]);
    if(a[1]==1) ins(0);
        else
    {
        for(int i=1;i<a[1];++i) ins(0);
        ins(1);
    }
        for(int i=2;i<=m;++i)
    {
            if(a[i-1]*2>=a[i]) for(int j=a[i-1]+1,k=a[i]-a[i-1];j<=a[i];++j) ins(ans[j-k]);
        else
        {
                for(int j=a[i]-2*a[i-1];j;--j) ins(0);
                if(!(p%(p-fail[p]))) --p,ins(1);
                for(int j=1;j<=a[i-1];++j) ins(ans[j]);
            }
        }
    for(int i=1;i<=n;++i) putchar(ans[i]+'0');
    puts("");
    }
}

以上是关于Luogu P3526 [POI2011]OKR-Periodicity的主要内容,如果未能解决你的问题,请参考以下文章

Luogu 3435 POI2006OKR-Periods of Words(kmp)

[POI2006][luogu3435] OKR-Periods of Words [kmp+next数组]

Luogu3516 POI2011 Shift 构造

1511: [POI2006]OKR-Periods of Words

[POI2006]OKR-Periods of Words

Luogu P3521 [POI2011]ROT-Tree Rotations