• 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;
    }
  • 相关阅读:
    IIS是如何处理ASP.NET请求的
    c# Socket通讯中关于粘包,半包的处理,加分割符
    windows2008(64位)下iis7.5中的url伪静态化重写(urlrewrite)
    C#微信公众号/订阅号开发 接口源码
    C#线程池多线程Socket通讯 服务器端和客户端示例
    百度地图JS调用示例
    c# 图片转二进制/字符串 二进制/字符串反转成图片
    电商项目面试总结
    96. Unique Binary Search Trees
    92.Reverse Linked List II
  • 原文地址:https://www.cnblogs.com/Narh/p/10822349.html
Copyright © 2020-2023  润新知