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 }

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/04/21/3034093.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值