我们的目的就是找到一个最大的L0,使得该串的90%可以被分成若干长度>L0的字典串中的子串。
明显可以二分答案,对于二分的每个mid
我们考虑dp:f[i]表示前i个字符,最多能匹配上多少个字符。
发现转移端点是个不断前进的区间,单调队列优化就可以了。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #define N 2222222 7 #define eps 1e-8 8 using namespace std; 9 int last,tot,ch[N][2],par[N],mx[N]; 10 void add(int c){ 11 int p=last,np=++tot;mx[np]=mx[p]+1; 12 for(;p&&!ch[p][c];p=par[p])ch[p][c]=np; 13 if(!p)par[np]=1; 14 else{ 15 int q=ch[p][c]; 16 if(mx[q]==mx[p]+1)par[np]=q; 17 else{ 18 int nq=++tot; 19 mx[nq]=mx[p]+1; 20 par[nq]=par[q]; 21 memcpy(ch[nq],ch[q],sizeof ch[nq]); 22 par[q]=par[np]=nq; 23 for(;p&&ch[p][c]==q;p=par[p])ch[p][c]=nq; 24 } 25 }last=np; 26 } 27 char s[N]; 28 int n,m,len; 29 int pp[N],f[N]; 30 void dfs(int v,int x,int l){ 31 int t=s[x+1]-'0';pp[x]=l; 32 if(x==len)return ; 33 if(ch[v][t])dfs(ch[v][t],x+1,l+1); 34 else{ 35 while(!ch[v][t])v=par[v],l=min(l,mx[v]); 36 dfs(ch[v][t],x+1,l+1); 37 } 38 } 39 int head,tail,q[N]; 40 bool check(int x){ 41 head=1;tail=0; 42 for(int i=1;i<=len;i++){ 43 f[i]=f[i-1]; 44 while(head<=tail&&q[head]<i-pp[i])head++; 45 if(head<=tail)f[i]=max(f[i],f[q[head]]+(i-q[head])); 46 if(i+1-x>=0){ 47 int t=i+1-x; 48 while(head<=tail&&f[t]-t>=f[q[tail]]-q[tail])tail--; 49 q[++tail]=t; 50 } 51 } 52 double ans=f[len],tot=len; 53 return ans>=(tot*0.9-eps); 54 } 55 int main(){ 56 mx[0]=-1; 57 for(int i=0;i<2;i++)ch[0][i]=1; 58 last=++tot; 59 scanf("%d%d",&n,&m); 60 while(m--){ 61 scanf("%s",s);len=strlen(s); 62 last=1;for(int j=0;j<len;j++)add(s[j]-'0'); 63 } 64 while(n--){ 65 scanf("%s",s+1); 66 len=strlen(s+1); 67 dfs(1,0,0); 68 int l=0,r=len,mid,ans=0; 69 while(l<=r){ 70 mid=(l+r)>>1; 71 if(check(mid)){ans=mid;l=mid+1;} 72 else r=mid-1; 73 } 74 printf("%d ",ans); 75 } 76 return 0; 77 }