当前位置: 代码迷 >> 综合 >> 【后缀自动机】【单调队列优化DP】BZOJ2806 Cheat
  详细解决方案

【后缀自动机】【单调队列优化DP】BZOJ2806 Cheat

热度:83   发布时间:2023-09-27 04:23:55.0

分析:

比较裸的后缀自动机题。

先求出每个前缀能匹配的最大后缀。然后二分答案做DP

f[i]=max{f[i?1],f[j]+i?j}(j∈[i?maxleni,i?k])f[i]=max\{f[i-1],f[j]+i-j\}(j\in [i-maxlen_i,i-k])f[i]=max{ f[i?1],f[j]+i?j}(j[i?maxleni?,i?k])
maxlenimaxlen_imaxleni?即以i为结尾的前缀的最大匹配后缀长度。

很显然j的取值右端点是单增的,稍微证明一下也会发现左端点也是单增的。

然后就可以单调队列优化DP了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define SF scanf
#define PF printf
#define MAXN 1100010
#define MAXS 3
using namespace std;
struct node{
    int len;node *ch[MAXS];node *fail;	
}SuA[MAXN],*last,*ncnt,*rt;	
void Insert(int c){
    node *p=last;node *np=++ncnt;np->len=p->len+1;last=np;while(p&&!p->ch[c])p->ch[c]=np,p=p->fail;if(!p)np->fail=rt;else{
    node *q=p->ch[c];if(q->len==p->len+1)np->fail=q;else{
    node *nq=++ncnt;*nq=*q;nq->len=p->len+1;np->fail=q->fail=nq;while(p&&p->ch[c]==q)p->ch[c]=nq,p=p->fail;}}
}
int res[MAXN];
void match(char s[],int N){
    node *now=rt;int len=0;for(int i=0;i<N;i++){
    int c=s[i]-'0';if(now->ch[c]){
    len++;now=now->ch[c];	}else{
    while(now&&!now->ch[c])now=now->fail;if(!now)now=rt,len=0;elselen=now->len+1,now=now->ch[c];}res[i]=len;}
}
int q[MAXN],tl,hd,f[MAXN];
int n,m;
bool check(int k,int len){
    hd=1;tl=0;for(int i=1;i<=len;i++){
    f[i]=f[i-1];if(i-k>=0){
    int add=i-k;while(hd<=tl&&f[q[tl]]-q[tl]<f[add]-add)tl--;q[++tl]=add;}while(hd<=tl&&q[hd]<i-res[i-1])hd++;if(hd<=tl)f[i]=max(f[i],f[q[hd]]-q[hd]+i);}return f[len]*10>=len*9;
}
void init(){
    rt=ncnt=last=SuA;	
}
char str[MAXN];
int main(){
    init();SF("%d%d",&n,&m);for(int i=0;i<m;i++){
    SF("%s",str);int len=strlen(str);for(int j=0;j<len;j++)Insert(str[j]-'0');Insert(2);	}for(int i=0;i<n;i++){
    SF("%s",str);int len=strlen(str);match(str,len);	int l=1,r=len,ans=0;while(l<=r){
    int mid=(l+r)>>1;if(check(mid,len)){
    ans=mid;l=mid+1;}elser=mid-1;}PF("%d\n",ans);}
}