10625:GNU = GNU'sNotUnix
問題文
http://uva.onlinejudge.org/external/106/10625.html
(1文字)->(文字列)
のように変換表が与えられる。
さらに、
(文字列) (1文字 x) (数字 n)
のようにクエリが与えられるので、文字列を変換表に従ってn回変換した時、文字列中に現れる文字xの個数を求めよ。
動的計画法で解いた。
dp[ ステップ数 ][ 文字 ] := 個数
v[ 文字 j ][ 文字 k ] := jを変換したときkができる個数
として、
i回目に現れるkの個数は
(i-1回目のjの個数)*(jを変換したときkができる個数)
だから基本は
dp[ i ][ k ] += dp[ i-1 ][ j ] * v[ j ][ k ]
で計算する。全てのv[ j ][ k ]が0だった時は
変換されないので前のステップでの文字がそのまま残る。つまり
dp[ i ][ j ]+=dp[ i-1 ][ j ]
#include <iostream> #include <algorithm> #include <vector> using namespace std; typedef unsigned long long ull; ull dp[10001][128]; int main(void){ int T,R; cin >> T; while(T--){ int v[128][128]; fill(v[0],v[128],0); cin >> R; cin.ignore(); for(int i=0;i<R;i++){ string str,to; char from; getline(cin, str); from=str[0]; to=str.substr(3); for(int i=0;i<to.size();i++) v[from-33][to[i]-33]++; } int Q; cin >> Q; while(Q--){ string source; char target; int t; cin >> source >> target >> t; fill(dp[0],dp[10001],0); for(int i=0;i<source.size();i++) dp[0][source[i]-33]++; for(int i=1;i<=t;i++){ for(int j=0;j<95;j++){ if(dp[i-1][j]<=0)continue; bool fg=false; for(int k=0;k<95;k++){ if(v[j][k]>0)fg=true; dp[i][k]+=dp[i-1][j] * v[j][k]; } if(!fg)dp[i][j]+=dp[i-1][j]; } } cout << dp[t][target-33] << endl; } } return 0; }