bzoj2806: [Ctsc2012]Cheat(SAM+DP)

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\n",ans);
92     }
93     return 0;
94 }

 

posted @ 2018-04-04 11:05  CHerish_OI  阅读(242)  评论(0编辑  收藏  举报