Description
给出一个字符串S,定义pre(S)为S的所有周期的集合。
求一个字典序最小的01串,使得这个01串的周期集合和pre(S)相同。
n<=200000
Solution
神仙构造题,波兰题怎么都这么仙啊
给出结论:周期<=>border,我们考虑原串的所有border
设将原串的所有border从小到大排序之后,满足前i个限制的串为Si,第i个border是ai,显然Si-1是Si的border
我们分两种情况讨论:
1:a[i-1]*2>=a[i],那么既然Si-1是Si的border又有重叠那么就直接将后面那部分接过来就行了
2::a[i-1]*2 < a[i],我们需要在中间补上一些东西,那么中间的那部分要么全0,要么是全0+一个1
证明可以看VFK的blog_ (:з」∠) _
现在问题变成了怎么判断是不是可以是全0
还有一个结论,如果S[i-1]+‘0’*j有一个循环节(d是S的周期且|d|整除|S|),那么是不合法的,否则是合法的
然后这个东西只需要判断最小正周期是否合法,也就是n-border是否合法
然后就O(n)了。。。。。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2e5+5;
int ty,n,tot,nxt[N],fail[N],s[N],a[N];
char st[N];
void push(int c) {
s[++tot]=c;
if (tot>1) {
int k=fail[tot-1];
while (k&&s[k+1]!=s[tot]) k=fail[k];
fail[tot]=k+=s[k+1]==s[tot];
}
}
int main() {
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
for(scanf("%d",&ty);ty;ty--) {
scanf("%s",st+1);n=strlen(st+1);
int k=0;
fo(i,2,n) {
while (k&&st[k+1]!=st[i]) k=nxt[k];
nxt[i]=k+=st[k+1]==st[i];
}
a[0]=tot=0;for(int x=n;x;x=nxt[x]) a[++a[0]]=x;
fo(i,1,a[0]/2) swap(a[i],a[a[0]-i+1]);
if (a[1]==1) push(0);
else {
fo(i,1,a[1]-1) push(0);
push(1);
}
fo(i,2,a[0]) {
if (a[i-1]*2>=a[i]) {
int k=a[i]-a[i-1];
fo(j,a[i-1]+1,a[i]) push(s[j-k]);
} else {
int k=a[i]-2*a[i-1];
fo(j,1,k) push(0);
if (!(tot%(tot-fail[tot]))) {
tot--;
push(1);
}
fo(j,1,a[i-1]) push(s[j]);
}
}
fo(i,1,n) putchar(s[i]+'0');
puts("");
}
return 0;
}