- 给定一个长度为(n)的字符串,求长度为(n)的字典序最小的(01)串,满足它们的周期集合相同。
- 数据组数(le20),(nle2 imes10^5)
(border)性质
众所周知,一个长度为(n)的字符串有长度为(k)的周期等价于有长度为(n-k)的(border)。
所以我们先利用(KMP)求出这个字符串的(border)集合,那么问题就变成要构造一个(01)串与它的(border)集合相同。
首先考虑如何构造长度最小的那个(border)(设为(p_{min})),若(p_{min}=1)就是一个( exttt{"0"}),否则是(p_{min}-1)个( exttt{"0"})后面跟上一个( exttt{"1"})。
然后考虑如何从(p_{lst})向(p_{now})扩展答案,分类讨论:
- 如果(2p_{lst}ge p_{now}),直接把原本答案串的最后(p_{now}-p_{lst})个字符复制一份即可。
- 如果(2p_{lst}<p_{now}),最好的情况当然是首尾填(p_{lst}),中间放(p_{now}-2p_{lst})个( exttt{"0"}),但这样可能会产生不应存在的周期。发现(p_{lst}+(p_{now}-2p_{lst}) imes exttt{"0"}+p_{lst})有不应存在的周期等价于(p_{lst}+(p_{now}-2p_{lst}) imes exttt{"0"})有长度为(m=p_{now}-p_{lst})约数的周期,也就是说(m-nxt_m)是(m)的约数。此时,只要把最后一个( exttt{"0"})改成( exttt{"1"})即可。
我们只要实时维护好答案串的(nxt)数组即可。
代码:(O(Tn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 200000
using namespace std;
int n,p[N+5];char s[N+5];
int m,nxt[N+5];char g[N+5];I void A(Cn char& x)//在末尾添加一个字符
{
if(g[++m]=x,m==1) return;RI i=nxt[m-1];W(i&&g[i+1]^x) i=nxt[i];nxt[m]=i+(g[i+1]==x);//求nxt
}
int main()
{
RI Tt,i,t;scanf("%d",&Tt);W(Tt--)
{
for(scanf("%s",s+1),n=strlen(s+1),m=0,i=1;i<=n;++i) A(s[i]);for(t=0,i=n;i;i=nxt[i]) p[++t]=i;//求出原串所有border
if(m=0,p[t]^1) {for(i=1;i^p[t];++i) A('0');A('1');}else A('0');W(--t)//求出初始答案串
{
if(2*p[t+1]>=p[t]) {for(i=p[t+1]-(p[t]-p[t+1])+1;i<=p[t+1];++i) A(g[i]);continue;}//直接把最后p[t]-p[t+1]个字符复制一份
for(i=1;i<=p[t]-2*p[t+1];++i) A('0');!(m%(m-nxt[m]))&&(--m,A('1'),0);for(i=1;i<=p[t+1];++i) A(g[i]);//检验能否在中间放p[t]-2*p[t+1]个0,不行则把最后的0改成1
}g[n+1]=0,puts(g+1);
}return 0;
}