P4022 [CTSC2012]熟悉的文章
题目大意
$m$个文本,$n$个模式串
对于每个模式串:
求最大L使得该串能分解成不小于$L$的子串,且在文本中出现的长度不小于该串总长度的$90%$
建广义后缀树
对于$L_1$,$L_2$两种情况,$L_1>L_2$,如果$L_1$符合,显然$L_2$一定符合
发现没有,答案是单调的,用二分$check$
$dp_i$为前$i$个字符能匹配的最大长度
$dp_i=max\{dp[j]+i-j\}$转换后$dp_i=max\{(dp[j]-j)+i\}$用单调队列维护就行
My complete code:
#include<cstdio> #include<string> #include<cstring> using namespace std; typedef long long LL; const LL maxn=400000; LL n,m,nod=1,last,Len; LL len[maxn],son[maxn][26],fail[maxn],val[maxn],dp[maxn]; char s[maxn]; inline void Insert(LL c){ LL p=last,np=++nod; last=np; len[np]=len[p]+1; while(p&&!son[p][c]){ son[p][c]=np; p=fail[p]; } if(!p) fail[np]=1; else{ LL q=son[p][c]; if(len[q]==len[p]+1) fail[np]=q; else{ LL nq=++nod; len[nq]=len[p]+1; memcpy(son[nq],son[q],sizeof(son[q])); fail[nq]=fail[q]; fail[q]=fail[np]=nq; while(p&&son[p][c]==q){ son[p][c]=nq; p=fail[p]; } } } } inline void Match(){ LL now=1,l=0; for(LL i=1;i<=Len;++i){ LL c=s[i]-‘0‘; while(now&&!son[now][c]){ now=fail[now]; l=len[now]; } if(now){ now=son[now][c]; ++l; }else{ now=1; l=0; } val[i]=l; } } inline bool check(LL L){ LL head=1,tail=0; LL que[maxn]; for(LL i=1;i<=L-1;++i) dp[i]=0; for(LL i=L;i<=Len;++i){ while(head<=tail&&dp[que[tail]]-que[tail]<dp[i-L]-(i-L)) --tail; que[++tail]=i-L; while(head<=tail&&que[head]<i-val[i]) ++head; dp[i]=dp[i-1]; if(head<=tail) dp[i]=max(dp[i],dp[que[head]]-que[head]+i); } return dp[Len]*10>=Len*9; } inline LL Solve(){ LL l=1,r=Len,ans=0; Match(); while(l<=r){ LL mid=(l+r)>>1; if(check(mid)){ ans=mid; l=mid+1; }else r=mid-1; } return ans; } int main(){ scanf("%lld%lld",&n,&m); while(m--){ scanf(" %s",s+1); Len=strlen(s+1); last=1; for(LL i=1;i<=Len;++i) Insert(s[i]-‘0‘); } while(n--){ scanf(" %s",s+1); Len=strlen(s+1); printf("%lld\n",Solve()); } }
原文地址:https://www.cnblogs.com/y2823774827y/p/10122570.html
时间: 2024-10-21 22:54:50