2806: [Ctsc2012]Cheat
题目:传送门
题解:
感觉这题考的更多的就是DP啊...
看完题目的第一反应就是广义SAM...(然而并不会)
再YY一会儿想起来可以直接将作文库连成一个母串(中间用2隔开)去跑SAM,然后直接把文章当成是子串在自动机上面跑(字符串匹配问题的套路啊)
考虑DP:设f[i]表示前i个位置的最大匹配长度。那么f[i]=max(f[i],f[j]+i-j);
直接DP肯定爆炸,再考虑利用SAM来预处理一个match[],match[i]表示当前文章以第i个位置结尾的子串和作文库匹配的最长长度(直接在自动机里面乱跑)
L满足而分性,直接二分,然后我们会发现对于f[i]有影响的其实只在于[i-match[i],i-L],i-L这个玩意肯定单调递增啊...
一波单调队列直接搞:对于队头,如果位置在合法区间内,则直接可以找到最优解来更新f[i]
槽点:一开始被卡精度...%AC_Artist.zig_zag之后又WA了...然后发现自己脑残在check里memset。。。(做了一上午...菜啊),数据真的有点水
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 struct SAM 8 { 9 int son[5],fail,dep; 10 SAM(){memset(son,0,sizeof(son));fail=dep=0;} 11 }ch[2110000];int a[2110000],match[1110000],root,cnt,last; 12 int n,m; 13 char s[1110000],st[1110000]; 14 void add(int k) 15 { 16 int x=a[k]; 17 int p=last,np=++cnt;ch[np].dep=ch[p].dep+1; 18 while(p && ch[p].son[x]==0)ch[p].son[x]=np,p=ch[p].fail; 19 if(p==0)ch[np].fail=root; 20 else 21 { 22 int q=ch[p].son[x]; 23 if(ch[q].dep==ch[p].dep+1)ch[np].fail=q; 24 else 25 { 26 int nq=++cnt;ch[nq]=ch[q]; 27 ch[nq].dep=ch[p].dep+1; 28 ch[np].fail=ch[q].fail=nq; 29 while(p && ch[p].son[x]==q)ch[p].son[x]=nq,p=ch[p].fail; 30 } 31 } 32 last=np; 33 } 34 void get_match(int len) 35 { 36 int p=root,sum=0; 37 for(int i=1;i<=len;i++) 38 { 39 int x=s[i]-'0'; 40 if(ch[p].son[x]!=0)sum++,p=ch[p].son[x]; 41 else //失配了就跳fail 42 { 43 while(p && ch[p].son[x]==0)p=ch[p].fail; 44 if(p==0)p=root,sum=0; 45 else sum=ch[p].dep+1,p=ch[p].son[x]; 46 } 47 match[i]=sum; 48 } 49 } 50 int f[1110000],list[1110000]; 51 bool check(int L,int len) 52 { 53 //if(L==0)return true; 54 f[0]=0;list[1]=0; 55 int p,head=1,tail=1; 56 for(int i=1;i<=len;i++) 57 { 58 f[i]=f[i-1];p=i-L;if(p<0)continue; 59 while(head<=tail && f[p]+i-p>f[list[tail]]+i-list[tail])tail--;list[++tail]=p; 60 while(head<=tail && list[head]<i-match[i])head++; 61 if(head<=tail)f[i]=max(f[i],f[list[head]]+i-list[head]); 62 } 63 double anss=double(f[len])/double(len); 64 return anss>=0.8999999999; 65 } 66 int main() 67 { 68 scanf("%d%d",&n,&m);int la=1; 69 cnt=0;root=last=++cnt; 70 for(int i=1;i<=m;i++) 71 { 72 scanf("%s",st+1);int len=strlen(st+1);int k=0; 73 for(int j=la;j<=len+la-1;j++) 74 { 75 k++;a[j]=st[k]-'0'; 76 add(j); 77 } 78 if(i!=m){la=len+la;a[la]=2;add(la);la++;} 79 } 80 while(n--) 81 { 82 scanf("%s",s+1);int len=strlen(s+1); 83 get_match(len); 84 int l=0,r=len,ans=0; 85 while(l<=r) 86 { 87 int mid=(l+r)/2; 88 if(check(mid,len))ans=mid,l=mid+1; 89 else r=mid-1; 90 } 91 printf("%d ",ans); 92 } 93 return 0; 94 }