bzoj2806 [Ctsc2012]Cheat
后缀自动机+dp,后缀自动机是用来求出给定文章最长的在模板中出现的字串的长度,这个很好做吧。把文章在模板上匹配就行了,记录每一位的最大匹配长度就行了。
然后我们二分答案,然后按照答案的限制求得最大匹配字符数。dp的方程很容易求得:f[i]=max{f[j]+i-j}。然后,假设二分的答案是limit,某位置的最大匹配长度是v[i],决策区间就是[i-v[i],i-limit],由于i-limit是逐步增加的,那么每一次只需要往队列里添加i-limit这个点,然后判断队首是否在决策区间里,即是否q[head]>=i-v[i]。那么此时单调队列里的元素都在决策区间里。这样就可以用队首元素的到最优的f[i]了。最后判断一下是否满足比例大于等于0.9即可。
我wa了无数次,原因是跪精度了......我把0.9换成0.899999999就过了,一下午啊!
cheat
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 1200000 7 #define inf 2147483647 8 using namespace std; 9 struct node 10 { 11 node *ch[3],*pre; 12 int mx; 13 }sam[maxn*2],*now,*rot; 14 char s[maxn],t[maxn]; 15 int v[maxn],q[maxn],f[maxn]; 16 int n,m,num; 17 18 void insert(int w) 19 { 20 node *p=now,*np=&sam[++num]; 21 np->mx=p->mx+1; 22 while (p&&p->ch[w]==0) p->ch[w]=np,p=p->pre; 23 if (!p) np->pre=rot; 24 else 25 { 26 node *q=p->ch[w]; 27 if (q->mx==p->mx+1) np->pre=q; 28 else 29 { 30 node *nq=&sam[++num]; 31 *nq=*q; 32 nq->mx=p->mx+1; 33 np->pre=q->pre=nq; 34 while (p&&p->ch[w]==q) p->ch[w]=nq,p=p->pre; 35 } 36 } 37 now=np; 38 } 39 40 void match(int len) 41 { 42 node *p=rot; 43 int cnt=0; 44 for (int i=0;i<len;i++) 45 { 46 int w=s[i]-'0'; 47 if (p->ch[w]) cnt++,p=p->ch[w]; 48 else 49 { 50 while (p&&p->ch[w]==0) p=p->pre;//失配?转向更小的后缀 51 if (p->ch[w]) cnt=p->mx+1,p=p->ch[w]; 52 else cnt=0,p=rot; 53 } 54 v[i+1]=cnt; 55 } 56 } 57 58 bool dp(int limit,int len) 59 { 60 f[0]=q[0]=0; 61 int head=1,tail=0,p; 62 for (int i=1;i<=len;i++) 63 { 64 f[i]=f[i-1]; 65 if ((p=i-limit)>=0) 66 { 67 while (head<=tail&&f[p]-p>f[q[head]]-q[head]) tail--; 68 q[++tail]=p; 69 } 70 while (head<=tail&&q[head]<i-v[i]) head++; 71 if (head<=tail) f[i]=max(f[i],f[q[head]]+i-q[head]); 72 } 73 double rate=(double)f[len]/len; 74 return rate>=0.8999999999; 75 } 76 77 int main() 78 { 79 //freopen("cheat.in","r",stdin); 80 //freopen("cheat.out","w",stdout); 81 scanf("%d%d",&n,&m); 82 rot=now=&sam[num=1]; 83 for (int i=1;i<=m;i++) 84 { 85 scanf("%s",t); 86 int len=strlen(t); 87 for (int i=0;i<len;i++) insert(t[i]-'0'); 88 insert(2); 89 } 90 for (int i=1;i<=n;i++) 91 { 92 scanf("%s",s); 93 int len=strlen(s); 94 match(len); 95 int l=0,r=len,ans=0; 96 while (l<=r) 97 { 98 int mid=(l+r)>>1; 99 if (dp(mid,len)) l=mid+1,ans=mid; 100 else r=mid-1; 101 } 102 printf("%d\n",ans); 103 } 104 return 0; 105 }
AC without art, no better than WA !