洛谷P4022 [CTSC2012]熟悉的文章 后缀自动机+二分+单调队列优化dp

洛谷P4022 [CTSC2012]熟悉的文章

题意

\(m\)\(01\)\(s_1,s_2,\dots,s_n\)\(n\)次询问,每次询问给出一个\(01\)\(x\),问是否存在一种分割方式将\(x\)分割成若干个子串使这些子串中长度大于等于\(L\)的且是\(s_1,s_2,\dots,s_n\)中任意一个的子串的总长度大于等于\(|x|\cdot 90\%\),问\(L\)最大为多少,若不存在输出\(0\)

分析

将这\(m\)个串用一个没出现过的字符连接起来建后缀自动机,二分\(L\)\(check\)就是对于\(x\)的每个前缀在自动机上匹配一个最长的后缀,设这个匹配长度为\(l\),若\(l>=L\),则有\(dp[i]=max\{dp[k]+i-k|k\in [i-l,i-L]\}\),考虑优化,要找到区间\([i-l,i-L]\)\(dp[k]+i-k\)的最大值然后转移,由于\(i\)是固定的,且对于每个前缀其区间左右端点是单调不减的,于是我们可以用单调队列来维护区间\(dp[k]-k\)的最大值,然后转移即可,最后判断\(dp[n]\cdot 10>=n\cdot 9\)来移动二分区间迭代找到答案。

Code

#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=3e6+10;
const int M=1e6+10;
const ll inf=1e18;
int T,n,m;
char s[N];
int q[N];
struct SAM{
    int last,cnt;int ch[N][3],fa[N],len[N];
    int dp[N];
    int newnode(){
        ++cnt;
        return cnt;
    }
    void insert(int c){
        int p=last,np=newnode();last=np;len[np]=len[p]+1;
        for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
        if(!p) fa[np]=1;
        else {
            int q=ch[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else{
                int nq=newnode();len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                fa[nq]=fa[q],fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
            }
        }
    }
    void init(){
        last=cnt=1;
    }
    bool solve(int L){
        int ln=strlen(s+1);
        rep(i,1,ln) dp[i]=0;
        int u=1,l=0;
        int dl=0,dr=-1;
        for(int i=1;i<=ln;i++){
            int j=i-L,c=s[i]-'0';
            if(j>=0){
                while(dl<=dr&&dp[j]-j>dp[q[dr]]-q[dr]) --dr;
                q[++dr]=j;
            }
            while(!ch[u][c]&&u!=1) u=fa[u],l=len[u];
            if(ch[u][c]) u=ch[u][c],l++;
            if(l>=L){
                while(dl<=dr&&q[dl]<i-l) ++dl;
                dp[i]=max(dp[i-1],dp[q[dl]]+i-q[dl]);
            }else dp[i]=dp[i-1];
        }
        return dp[ln]*10>=ln*9;
    }
}sam;
int main(){
    scanf("%d%d",&n,&m);
    sam.init();
    rep(i,1,m){
        scanf("%s",s+1);
        int len=strlen(s+1);
        for(int j=1;j<=len;j++) sam.insert(s[j]-'0');
        sam.insert(2);
    }
    rep(i,1,n){
        scanf("%s",s+1);
        int l=1,r=2000000;
        while(l<=r){
            int mid=l+r>>1;
            if(sam.solve(mid)) l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",r);
    }
    return 0;
}
posted @ 2020-11-05 21:56  xyq0220  阅读(62)  评论(0编辑  收藏  举报