bzoj 2806 [Ctsc2012]Cheat——广义后缀自动机+单调队列优化DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2806

只想着怎么用后缀数据结构做,其实应该考虑结合其他算法。

可以二分那个长度 L 。设当前二分为 mid ;令 dp[ i ] 表示到 i 位置“熟悉”的最大长度。那么 \( dp[i]=\max(dp[i-1],\max\limits_{j<=i-mid,s[j+1...i] \in S}(dp[j]+(i-j)) ) \) (其中 S 是模式串的所有子串集合)。

关于那个判断,只要先作出以询问串的每个位置 i 为结尾最长能匹配的后缀长度 f[ i ] 就行了。

这个 DP 过程可以用单调队列优化。在 i 位置把 i-mid 的值加入队列。

注意匹配的长度不是自动机对应点的 len ,而是要记一个 ct ,如果顺延的话,ct++ 而不是 ct = len[ go[cr][w] ] 这样。

注意 dp 的时候如果 i < mid ,不仅是不往队列加元素,还不能做转移(比如 s[ 1...i ] 在模式串里出现也不能给 dp[ i ] 赋值)!因为这样匹配上的是长度 <mid 的,不合法。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int Mx(int a,int b){return a>b?a:b;}
const int N=11e5+5;
int n,m,go[N][2],len[N],fa[N],cnt=1,lst;
int f[N],dp[N],q[N],he,tl; char s[N];
int cz(int p,int w)
{
  int q=go[p][w],nq=++cnt; len[nq]=len[p]+1;
  fa[nq]=fa[q]; fa[q]=nq;
  memcpy(go[nq],go[q],sizeof go[q]);
  for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
  return nq;
}
int ins(int p,int w)
{
  if(go[p][w])
    {
      int q=go[p][w];
      if(len[q]==len[p]+1)return q;
      return cz(p,w);
    }
  int np=++cnt; len[np]=len[p]+1;
  for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
  if(!p){ fa[np]=1;return np;}//fa=1!
  int q=go[p][w];
  if(len[q]==len[p]+1)fa[np]=q; else fa[np]=cz(p,w);
  return np;
}
int chk(int mid,int d)
{
  he=tl=0;
  for(int i=1;i<=d;i++)
    {
      dp[i]=dp[i-1];
      if(i<mid)continue;//not do!!!
      int cr=i-mid;
      while(he<tl&&dp[q[tl]]-q[tl]<=dp[cr]-cr)tl--;
      q[++tl]=cr;
      while(he<tl&&q[he+1]<i-f[i])he++;
      if(he<tl)dp[i]=Mx(dp[i],dp[q[he+1]]+i-q[he+1]);
    }
  return dp[d];
}
int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)
    {
      scanf("%s",s+1);
      for(int j=1,lm=strlen(s+1),d=1;j<=lm;j++)
    d=ins(d,s[j]-'0');
    }
  for(int i=1,d;i<=n;i++)
    {
      scanf("%s",s+1);
      d=strlen(s+1); 
      for(int j=1,cr=1,ct=0;j<=d;j++)//ct not len[cr]!!
    {
      int w=s[j]-'0';
      while(!go[cr][w])cr=fa[cr],ct=len[cr];
      if(go[cr][w])cr=go[cr][w],ct++;
      else cr=1,ct=0;
      f[j]=ct;
    }
      int lm=ceil(0.9*d),l=1,r=d,ret=0;
      while(l<=r)
    {
      int mid=l+r>>1;
      if(chk(mid,d)>=lm)ret=mid,l=mid+1;
      else r=mid-1;
    }
      printf("%d\n",ret);
    }
  return 0;
}

 

posted on 2019-03-27 17:49  Narh  阅读(165)  评论(0编辑  收藏  举报

导航