zoukankan      html  css  js  c++  java
  • [ CERC 2014 ] Vocabulary


    (\)

    (Description)


    给出三个长度分别为 (lenA,lenB,lenC) 的三个字符串 (A,B,C) ,其中字符集只包括所有小写字母以及 (?) 号。

    现在将所有 (?) 号改为任意小写字母,问有多少种方案,使得 (A,B,C) 三个字符串按照字典序排列(()相同不算()?)

    输出方案数对 (10^9+9) 取模后的结果。多组询问。

    • 字符串总长度(le 10^6)

    (\)

    (Solution)


    有趣的预处理题目。

    三个串长度不能确定,不能通过数据组数判断复杂度要求,只好按照上界分析,即只有一组数据,做法接近线性。

    首先考虑三个串不等长的问题,字典序中规定,一个串的任意长度前缀都比当前串字典序小,所以在不是最长的串后面补上若干个比 (a) 还要小的字符即可。

    (\)

    考虑为了使得最后合法,三个串的字典序在确定 (?) 号的时候合法的情况(小于号表示字典序左侧优于右侧):

    • (1: A=B=C)
    • (2: A<B=C)
    • (3: A=B<C)
    • (4: A<B<C)

    其他情况会导致在某一位置某两串字典序相反,这样后面不管怎样设置字典序都是反过来的。

    (\)

    动态规划。

    (f[i][1/2/3/4])表示,当前位置为(i),当前位置及以前的所有 (?) 号都确定完了之后,使得三个串的字典序关系变为 (1/2/3/4) 的方案数。

    为了便于初始化,将字符串都向后推一个,然后有(f[0][1]=1),答案为(f[maxlen][4])

    考虑转移的过程,合法的转移有:(1 o 1/2/3/4 , 2 o 2/4 , 3 o 3/4 , 4 o4),想不明白可以手玩一下,当前位置每一种情况的字典序的前提是不同的,例如 (1) 类情况必须要求前面的位置都为 (1) 类情况。

    (\)

    然后考虑转移的过程,显然我们的转移影响要素有,前一位置状态,以及当前位置三个字符串的字符。

    考虑到每次操作统计方案数非常麻烦,而转移只有 (28^3 imes(4+2+2+1))(()后面括号里对应着状态间的转移()),不妨对每个转移处理出来方案数。

    (g[i][j][x][y][z]) 表示,要从状态 (i) 转移到 (j) ,当前位置三个串的字符分别为 (x,y,z) 的方案数,预处理就暴力枚举三个字符,注意从上到下字符区间的限制,然后将可以累加的转移方案数累加就好。

    然后我们动规的转移就可以直接借用 (g) 数组转移了,单组数据复杂度 ( ext O (16N))

    (\)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 1000010
    #define R register
    #define mod 1000000009
    using namespace std;
    typedef long long ll;
    
    char a[N],b[N],c[N];
     
    ll f[N][4],g[4][4][28][28][28];
     
    int s1[N],s2[N],s3[N],lena,lenb,lenc,len;
     
    inline void prework(){
      int l1,r1,l2,r2,l3,r3;
      for(R int i=0;i<=27;++i)
        for(R int j=0;j<=27;++j)
          for(R int k=0;k<=27;++k){
            (i==27)?l1=1,r1=26:l1=r1=i;
            for(R int x=l1;x<=r1;++x){
              (j==27)?l2=1,r2=26:l2=r2=j;
              for(R int y=l2;y<=r2;++y){
                (k==27)?l3=1,r3=26:l3=r3=k;
                for(R int z=l3;z<=r3;++z){
                  ++g[3][3][i][j][k];
                  if(x==y) ++g[1][1][i][j][k];
                  if(x<y) ++g[1][3][i][j][k];
                  if(y==z) ++g[2][2][i][j][k];
                  if(y<z) ++g[2][3][i][j][k];
                  if(x==y&&y==z) ++g[0][0][i][j][k];
                  if(x==y&&y<z) ++g[0][1][i][j][k];
                  if(x<y&&y==z) ++g[0][2][i][j][k];
                  if(x<y&&y<z) ++g[0][3][i][j][k];
                }
              }
            }
          }
    }
     
    inline void init(){
      scanf("%s",a); lena=strlen(a);
      scanf("%s",b); lenb=strlen(b);
      scanf("%s",c); lenc=strlen(c);
      len=max(lena,max(lenb,lenc));
      for(R int i=0;i<lena;++i) s1[i+1]=(a[i]=='?'?27:a[i]-'a'+1);
      for(R int i=lena;i<=len;++i) s1[i+1]=0;
      for(R int i=0;i<lenb;++i) s2[i+1]=(b[i]=='?'?27:b[i]-'a'+1);
      for(R int i=lenb;i<=len;++i) s2[i+1]=0;
      for(R int i=0;i<lenc;++i) s3[i+1]=(c[i]=='?'?27:c[i]-'a'+1);
      for(R int i=lenc;i<=len;++i) s3[i+1]=0;
    }
     
    int main(){
      prework();
      int t;
      scanf("%d",&t);
      while(t--){
        init();
        for(R int i=0;i<=len;++i)
          for(R int j=0;j<4;++j) f[i][j]=0;
        f[0][0]=1;
        for(R int i=1;i<=len;++i)
          for(R int k=0;k<4;++k)
            if(f[i-1][k]) for(R int j=0;j<4;++j)
              (f[i][j]+=f[i-1][k]*g[k][j][s1[i]][s2[i]][s3[i]])%=mod;
        printf("%lld
    ",f[len][3]);
      }
      return 0;
    }
    
  • 相关阅读:
    体检套餐管理系统
    在这个与金沂同桌的日子里,我在北大青鸟学习了 第四章 深入类的方法 下面是我的上机3
    总结
    今天晚上雨夹雪,爱人狠心把我撇。今夜孤独一个人,只好来把代码写。 欢迎阅读我的第四章笔记 深入类的方法
    我在北京写代码 写出心中悲与喜 写出人间的悲欢离合 欢迎阅读 我的第三章 使用集合组织相关数据(泛型集合)
    在那个春暖花开的季节 今天微微的小雨 伴着轻轻的晚风我们一起来编写 员工考勤信息管理
    非泛型集合
    经理评分系统
    模仿魔兽登录界面 编程小练习
    【BZOJ 3524】【Poi2014】Couriers 可持久化线段树
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9751265.html
Copyright © 2011-2022 走看看