1. 程式人生 > 實用技巧 >[CF526D] Om Nom and Necklace - 擴充套件kmp,差分

[CF526D] Om Nom and Necklace - 擴充套件kmp,差分

Description

給定一個長度為 \(n\) 的字串 \(S\),求它的所有字首中,哪些可以被劃分為 \(ABAB...ABA\) 的形式,其中 \(A,B\) 是可以為空的串,且一共包含了 \(k\)\(AB\)

Solution

考慮令 \(S=AB\),列舉 \(S\) 的長度 \(i\),我們只需要檢查 \(LCP(ji+1,1) \ge i, j=1..k-1\),如果都滿足,則可以將答案序列中往後的一段覆蓋為 \(1\)

用擴充套件 KMP 可以在 \(O(n)\) 時間內完成對 \(LCP(i,1), i=1..n\) 的計算

所有被列舉的長度共有 \(O(n/k)\)

個,每次需要檢查 \(O(k)\) 個位置,故總複雜度 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1000005;

namespace exkmp
{
int nxt[N]; // x[0..m-1] x[i..m-1] LCP
int ext[N]; // x[0..m-1] y[i..n-1] LCP
void presolve(const char* x,int m)
{
    nxt[0]=m;
    int j=0;
    while(j+1<m&&x[j]==x[j+1]) j++;
    nxt[1]=j;
    int k=1;
    for(int i=2; i<m; i++)
    {
        int p=nxt[k]+k-1, l=nxt[i-k];
        if(i+l<p+1) nxt[i]=l;
        else
        {
            j=max(0ll,p-i+1);
            while(i+j<m && x[i+j]==x[j]) j++;
            nxt[i]=j;
            k=i;
        }
    }
}
void presolve(string s)
{
    presolve(s.c_str(),s.length());
}
int lcp(int p)
{
    return nxt[p-1];
}
} // namespace exkmp

int n,k;
string s;

namespace sumsolver
{
int a[N];
void modify(int l,int r)
{
    r=min(r,n);
    l=max(l,1ll);
    a[l]++;
    a[r+1]--;
}
void solve()
{
    for(int i=1;i<=n;i++) a[i]+=a[i-1];
}
int query(int p)
{
    return a[p]?1:0;
}
}

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n>>k>>s;
    exkmp::presolve(s);

    for(int i=1;i*k<=n;i++)
    {
        int flag=1;
        for(int j=1;j<k;j++)
        {
            if(exkmp::lcp(i*j+1)<i)
            {
                flag=0;
                break;
            }
        }
        if(flag)
        {
            sumsolver::modify(i*k,i*k+min(exkmp::lcp(i*k+1),i));
        }
    }

    sumsolver::solve();
    for(int i=1;i<=n;i++) cout<<sumsolver::query(i);
}