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