[CTSC2012]熟悉的文章

题意

有多个主串,每次询问将询问串分成多个连续子串,如果一个子串长度 L ≥ L 且在主串中出现过就是合法的

如果合法的子串总长度 询问串长的 90% 90 % ,这个串就是合法的字符串,求使得询问串成为合法的字符串的最大的 L L


题解

先建好广义SAM,发现 L L 满足单调性,所以可以二分答案

考虑怎么check用dp啊

考虑一个暴力的 DP,f[i] D P , f [ i ] 表示前 i i 个字符最长合法长度,maci表示匹配到 i i 的最长公共子串长度(在广义SAM上,求法同 [Spoj1812]LCS2 [ S p o j 1812 ] L C S 2 )

f[i]=max{f[i1],f[j]+ji,j[imaci,iL]} ⇒ f [ i ] = m a x { f [ i − 1 ] , f [ j ] + j − i , j ∈ [ i − m a c i , i − L ] }

可以发现 iL i − L 单调递增的, imaci i − m a c i 是单调不增的,所以可以使用单调队列优化到 O(n) O ( n )

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e6+5,M=2*N;
typedef long long ll;
typedef int arr[M];
struct SAM{
    int las,T,ch[M][2];arr fa,len;
    SAM(){las=T=1;}
    inline void ins(int c){
        int p=las,np;fa[las=np=++T]=1,len[np]=len[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(p){
            int q=ch[p][c],nq;
            if(len[p]+1==len[q])fa[np]=q;
            else{
                fa[nq=++T]=fa[q],len[nq]=len[p]+1,memcpy(ch[nq],ch[q],4*2);
                for(fa[q]=fa[np]=nq;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
    }
    int n,mac[N],f[N],q[N];char s[N];
    inline void Match(){
        int x=1,l=0,c;
        fp(i,1,n){
            c=s[i]-'0';while(x&&!ch[x][c])x=fa[x],l=len[x];
            if(x)x=ch[x][c],++l;else x=1,l=0;mac[i]=l;
        }
    }
    inline bool chk(int L){
        int h=1,t=0;
        fp(i,1,L-1)f[i]=0;
        fp(i,L,n){f[i]=f[i-1];
            while(h<=t&&f[q[t]]-q[t]<f[i-L]-(i-L))--t;
            q[++t]=i-L;while(h<=t&&q[h]<i-mac[i])++h;
            if(h<=t)cmax(f[i],f[q[h]]+i-q[h]);
        }
        return f[n]*10>=n*9;
    }
    inline void sol(){
        scanf("%s",s+1);n=strlen(s+1);Match();
        int L=0,R=n,ans=0,mid;
        while(L<=R){
            mid=(L+R)>>1;
            if(chk(mid))ans=mid,L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",ans);
    }
}p;
int n,m;char s[N];
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    scanf("%d%d",&n,&m);
    fp(i,1,m){
        scanf("%s",s),p.las=1;
        fp(j,0,strlen(s)-1)p.ins(s[j]-'0');
    }
    while(n--)p.sol();
return Ot(),0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值