题解:P3526 [POI 2011] OKR-Periodicity

申必题

题意

对于一个字符串 \(S\),求出一个字典序最小的 01 串 \(T\),满足 \(|S|=|T|\)\(S\)\(T\) 的所有周期相同。

思路

首先周期可以用 KMP 跳 border 求出。于是把限制转化为 \(S\)\(T\) 从结尾开始往前跳最长 border 的每个位置上的最长 border 都相同。

考虑递归构造。设 \(f(x)\) 表示长为 \(x\) 的前缀的答案。假设此时已经求出 \(f(fail(x))\),要求 \(f(x)\)。分类讨论:

  1. \(2fail(x)\ge x\),此时 \(f(x)\) 可以唯一确定。
  2. \(2fail(x)< x\),此时中间剩余的部分需要被构造。

首先对于第一种情况,我们证明 \(f(x)\) 一定合法,即不存在更长的 border。如果 \(f(x)\) 存在更长的 border,那么就存在一个更小的周期,且这个周期一定也在 \(f(fail(x))\) 中存在。然后 \(f(fail(x))\)\(S_{1,\cdots,fail(x)}\) 的所有周期相同,所以 \(f(x)\) 更长的这个 border 必定在 \(S_{1,\cdots,x}\) 中也是 border。这和 \(fail(x)\)\(S_{1,\cdots,x}\) 的最长 border 矛盾,因此 \(f(x)\) 一定合法。

对于第二种情况,我们要找到字典序最小的构造。我们声称中间部分一定会是 \(0^{len}\)\(0^{len-1}1\)。下面给出证明:

设新产生的 border 长为 \(y\)。如果 \(fail(x)+y>x\),说明新 border 和原 border 有交,可以仿照第一种情况来证明这种串唯一。

考虑 \(fail(x)+y\le x\) 的情况。此时只有中间串全 \(0\)\(f(fail(x))\)\(0\) 或中间串等于 \(f(fail(x))\) 两种。\(0^{len}\)\(0^{len-1}1\) 中必有一个不满足。

因此一定存在这样的构造合法。

代码

按上面过程模拟即可。

#include <bits/stdc++.h>
using namespace std;
constexpr int N=2e5+5;
int n,nxt[N];
bool a[N];
bool check(int len){
    vector<int>f(len+1);
    for(int i=2;i<=len;i++){
        f[i]=f[i-1];
        while(f[i]&&a[f[i]+1]!=a[i])f[i]=f[f[i]];
        if(a[f[i]+1]==a[i])f[i]++;
    }
    for(int i=len;i;i=nxt[i])if(f[i]!=nxt[i])return 0;
    return 1;
}
void solve(){
    string s;cin>>s,n=s.size();
    for(int i=2;i<=n;i++){
        nxt[i]=nxt[i-1];
        while(nxt[i]&&s[nxt[i]]!=s[i-1])nxt[i]=nxt[nxt[i]];
        if(s[nxt[i]]==s[i-1])nxt[i]++;
    }
    vector<int>p;
    for(int i=n;i;i=nxt[i])p.push_back(i);
    for(int i=p.back()-1;i>=1;i--)a[i]=0;
    a[p.back()]=(p.back()>1);
    for(int i=p.size()-2;i>=0;i--){
        int len=p[i],flen=p[i+1];
        if(flen*2>len)for(int i=len;i>flen;i--)a[i]=a[flen-(len-i)];
        else{
            for(int i=1;i<=flen;i++)a[len-(flen-i)]=a[i];
            for(int i=flen+1;i<=len-flen;i++)a[i]=0;
            if(!check(len))a[len-flen]=1;
            assert(check(len));
        }
    }
    for(int i=1;i<=n;i++)cout<<a[i];cout<<'\n';
}
signed main(){
    clock_t _st=clock();
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t;cin>>t;
    while(t--)solve();
    clock_t _ed=clock();
    cerr<<(_ed-_st)*1.0/CLOCKS_PER_SEC<<'\n';
    return 0;
}
posted @ 2026-04-14 11:24  Redolent  阅读(3)  评论(0)    收藏  举报