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;
}