【洛谷3526】[POI2011] OKR-Periodicity(border)

点此看题面

  • 给定一个长度为(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;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3526.html