CF 1150 D Three Religions——序列自动机优化DP

题目:http://codeforces.com/contest/1150/problem/D

老是想着枚举当前在给定字符串的哪个位置,以此来转移。

所以想对三个串分别建 trie 树,然后求出三个trie树上各选一个点的答案。那么从“在三个trie树的根,在给定字符串的0位置”开始扩展。

当然 TLE 了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
const int N=1e5+5,M=1005;
int n,m,tot=3,nw[5],fa[M],c[M][30],fl[M],q2[M];
char s[N];
struct Dt{
  int x,y,z;
  Dt(int x=0,int y=0,int z=0):x(x),y(y),z(z) {}
  bool operator< (const Dt &b)const
  {
    if(x!=b.x)return x<b.x;
    if(y!=b.y)return y<b.y; return z<b.z;
  }
  bool operator== (const Dt &b)const
  {return x==b.x&&y==b.y&&z==b.z;}
}qr[M],I;
struct Node{
  Dt a;int ps;
  Node(Dt a=I,int p=0):a(a),ps(p) {}
  bool operator< (const Node &b)const
  {return a<b.a;}
};
queue<Node> q;
map<Dt,bool> mp;
void add(int &x,int w)
{
    if(c[x][w])x=c[x][w];////if!!!!!
    else{ c[x][w]=++tot; fa[tot]=x; x=tot;}
}
void get_fl(int rt)
{
  int he=0,tl=0;
  for(int i=0;i<26;i++)
    if(c[rt][i])fl[c[rt][i]]=rt,q2[++tl]=c[rt][i];
    else c[rt][i]=rt;
  while(he<tl)
    {
      int k=q2[++he],pr=fl[k];
      for(int i=0;i<26;i++)
    if(c[k][i])fl[c[k][i]]=c[pr][i],q2[++tl]=c[k][i];
    else c[k][i]=c[pr][i];
      
    }
}
void cz(Dt cr,int ps)
{
  for(int x=cr.x;x;x=fl[x])
    for(int y=cr.y;y;y=fl[y])
      for(int z=cr.z;z;z=fl[z])
    if(!mp[cr]){mp[cr]=1; q.push(Node(cr,ps));}
    else return;
}
int main()
{
  scanf("%d%d",&n,&m); scanf("%s",s+1);
  nw[1]=1; nw[2]=2; nw[3]=3;
  char op,w;int d;
  for(int i=1;i<=m;i++)
    {
      cin>>op; scanf("%d",&d);
      if(op=='+'){cin>>w;add(nw[d],w-'a');}
      else nw[d]=fa[nw[d]];
      qr[i]=Dt(nw[1],nw[2],nw[3]);
    }
  for(int i=1;i<=3;i++)get_fl(i);
  Dt cr=Dt(1,2,3); mp[cr]=1; q.push(Node(cr,0));
  while(q.size())
    {
      Node k=q.front();q.pop();
      if(k.ps==n)continue;
      k.ps++; q.push(k); int w=s[k.ps]-'a';
      cr=k.a; cr.x=c[cr.x][w]; cz(cr,k.ps);
      cr=k.a; cr.y=c[cr.y][w]; cz(cr,k.ps);
      cr=k.a; cr.z=c[cr.z][w]; cz(cr,k.ps);
    }
  for(int i=1;i<=m;i++)
    puts(mp.count(qr[i])?"YES":"NO");
  return 0;
}
View Code

给定字符串的一个位置可能使得三个串都不能扩展。所以考虑不枚举字符串的每个位置来转移,而是做一个序列自动机。

令 dp[i][j][k] 表示三个地区分别匹配了前 i 、j、k 个字符的“最靠前位置”,如果值==n+1说明无解。

那么就可以使用序列自动机实现 2503 的DP了。就是 dp[i][j][k] = min( nxt[ dp[i-1][j][k] ][ c1[i] ] , nxt[ dp[i][j-1][k] ][ c2[j] ] , nxt[ dp[i][j][k-1] ][ c3[k] ] ) 。

对于一个询问,如果是 ' - ' ,DP数组不用改动。如果是 ' + ' ,固定该维, 2502 做一下DP即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
const int N=1e5+5,M=255,K=26;
int n,lst[K],nxt[N][K],dp[M][M][M],l0,l1,l2;
char s[N];int c0[M],c1[M],c2[M];
int main()
{
  n=rdn(); int Q=rdn();
  scanf("%s",s+1);
  for(int i=0;i<K;i++)
    lst[i]=nxt[n+1][i]=n+1;
  for(int i=n;i>=0;i--)
    {
      for(int j=0;j<K;j++) nxt[i][j]=lst[j];
      if(i)lst[s[i]-'a']=i;
    }
  char op;int x,w;
  while(Q--)
    {
      cin>>op; x=rdn()-1;
      if(op=='+')
    {
      cin>>op; w=op-'a';
      if(x==0)
        {
          c0[++l0]=w;
          for(int i=0;i<=l1;i++)
        for(int j=0;j<=l2;j++)
          {
            dp[l0][i][j]=nxt[dp[l0-1][i][j]][w];
            if(i)dp[l0][i][j]=
               Mn(dp[l0][i][j],nxt[dp[l0][i-1][j]][c1[i]]);//i not l1
            if(j)dp[l0][i][j]=
               Mn(dp[l0][i][j],nxt[dp[l0][i][j-1]][c2[j]]);
          }
        }
      if(x==1)
        {
          c1[++l1]=w;
          for(int i=0;i<=l0;i++)
        for(int j=0;j<=l2;j++)
          {
            dp[i][l1][j]=nxt[dp[i][l1-1][j]][w];
            if(i)dp[i][l1][j]=
               Mn(dp[i][l1][j],nxt[dp[i-1][l1][j]][c0[i]]);
            if(j)dp[i][l1][j]=
               Mn(dp[i][l1][j],nxt[dp[i][l1][j-1]][c2[j]]);
          }
        }
      if(x==2)
        {
          c2[++l2]=w;
          for(int i=0;i<=l0;i++)
        for(int j=0;j<=l1;j++)
          {
            dp[i][j][l2]=nxt[dp[i][j][l2-1]][w];
            if(i)dp[i][j][l2]=
               Mn(dp[i][j][l2],nxt[dp[i-1][j][l2]][c0[i]]);
            if(j)dp[i][j][l2]=
               Mn(dp[i][j][l2],nxt[dp[i][j-1][l2]][c1[j]]);
          }
        }
    }
      else
    {
      if(x==0)l0--; if(x==1)l1--; if(x==2)l2--;
    }
      puts(dp[l0][l1][l2]<=n?"YES":"NO");
    }
  return 0;
}

 

posted on 2019-05-06 21:47  Narh  阅读(233)  评论(0编辑  收藏  举报

导航