UAPC 2011 I 11224111122411

問題

携帯電話のひらがな入力のような装置がある。
ただし、同じキーを続けて押した時に、どこで切れ目があると解釈されるかはわからない。
また、同じキーを何回も押すと、あ→い→う→え→お→あ のように文字がループする。


キーの入力が与えられたとき、出力される文字列として考えられるのは何通りか。

方針

まずは文字をループさせない(余計な入力がない)場合の文字列を数える。
これは、今までにキーを何回押したか、最後の切れ目にキーがいくつあるかを数えるDPで求まる。
dp[i+1][j+1] += dp[i][j]
dp[i+1][1] += dp[i][j]
みたいな漸化式。


そして、余計な文字を入れることを考える。
6回少なくキーを押した文字列というのは作れる。12回少ないのも……と作れるので、
それらを全部足せばいいことがわかる。
連続する文字の場合の数がわかったら、あとは掛け算すればいい。

ソースコード

const int mod=100000007;
int dp[100001][6],dp2[100001][4];
int sum[100001],sum2[100001];
string in;

int main(){
  dp[0][0]=dp2[0][0]=1;
  
  rep(i,100000)rep(j,6){
    if(j<5)(dp[i+1][j+1]+=dp[i][j])%=mod;
    (dp[i+1][1]+=dp[i][j])%=mod;
  }
  rep(i,100000)rep(j,4){
    if(j<3)(dp2[i+1][j+1]+=dp2[i][j])%=mod;
    (dp2[i+1][1]+=dp2[i][j])%=mod;
  }
  rep(i,100001){
    rep(j,6)(sum[i]+=dp[i][j])%=mod;
    rep(j,4)(sum2[i]+=dp2[i][j])%=mod;
  }
  rep(i,100001){
    if(i>5)(sum[i]+=sum[i-6])%=mod;
    if(i>3)(sum2[i]+=sum2[i-4])%=mod;
  }
  
  while(getline(cin,in),in!="#"){
    int cnt=0;
    vi v,type;
    rep(i,in.size()){
      if(i&&in[i]!=in[i-1]){
        v.pb(cnt); cnt=1;
        type.pb(in[i-1]=='0'||in[i-1]=='8');
      }
      else cnt++;
    }
    type.pb(in[in.size()-1]=='0'||in[in.size()-1]=='8');
    v.pb(cnt);

    ll ans=1;
    rep(i,v.size()){
        if(!type[i])(ans*=sum[v[i]-1])%=mod;
        else (ans*=sum2[v[i]-1])%=mod;
    }
    cout<<ans<<endl;
  }
  return 0;
}