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=\begin{cases}0&a_1=1\\(a_1-1)*0+1&a_1>1\end{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("");
    }
}
posted @ 2020-02-11 11:03  Shiina_Mashiro  阅读(322)  评论(0编辑  收藏  举报