ARC 059 F - バイナリハック / Unhappy Hacking
問題
F: バイナリハック / Unhappy Hacking - AtCoder Regular Contest 059 | AtCoder
問題概要
キーボードには0
,1
,B
の3種類のキーがある。B
は空文字列でなければ末尾の一文字を削除する。このキーボードのキーを回押して文字列を入力したい時、入力方法は何通り有るか。
アイデア
単純な全探索を考えると、キーを押した回数と現在の文字列の状態を保存することが考えられる。これだと状態数が多すぎるので状態数を減らることを考えてみると、文字列全体を保存する必要はなく、「現在の文字列の長さ」と「何文字目までが合致しているか」を保存すればよいということになる。これで計算量がになり、部分点を取れる(submission)。
これでは満点を得ることは出来ないので、さらに状態数を減らすことを考えたい。ここで、文字列に注目してみると、文字列は0
と1
しか文字列に含まないことから、文字列と同じ長さの文字列は通りあることになる。そして、この長さの文字列を完成させるために必要な操作の組み合わせはどれも同じ数だけ存在する。よって、現在の状態を考えるのをやめて、回キーを操作した時に最終的に長さの文字列が得られる入力方法の組み合わせを求めて、で割ったものがその特定の文字列を作れる組み合わせだということになり、計算量はに抑えられ、間に合う。
実装(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define each(itr,c) for(__typeof(c.begin()) itr=c.begin(); itr!=c.end(); ++itr) #define all(x) (x).begin(),(x).end() #define pb push_back #define fi first #define se second ll mod=1e9+7; ll mod_pow(int x, int n) { ll pw[31]; pw[0]=x; for(int i=1; i<31; ++i) pw[i]=(pw[i-1]*pw[i-1])%mod; ll ret=1; rep(i,31) { if(n&(1<<i)) (ret*=pw[i])%=mod; } return ret; } ll mod_inv(int x) { return mod_pow(x,mod-2); } ll dp[5001][5001]={0}; int main() { int n; string s; cin >>n >>s; int S=s.size(); dp[0][0]=1; rep(i,n)rep(j,i+1) { (dp[i+1][j+1]+=(dp[i][j]*2)%mod)%=mod; (dp[i+1][max(0,j-1)]+=dp[i][j])%=mod; } ll ans=dp[n][S]; ans*=mod_inv(mod_pow(2,S)); cout << ans%mod << endl; return 0; }