[bzoj2528][POI2011]Periodicity

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章