読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

JOI春合宿 2011-day3 deciphering

JOI 動的計画法 文字列

問題文 : 解読

解法 :
dp[i][j], i番目の文字まで, jで終わる暗号の総数, とすると

dp[i][i番目の文字] = Σ[j = 'A'...'Z' + ' ', j + i番目の文字は禁止されていない] dp[i - 1][j]

となります. 答えは Σ[i = 'A'...'Z'] dp[L][i] です.
dp[0][' '] = 1としておけば, あとはテーブル使い回しのDPで解けます.

計算量は O(L * 26) = O(L)です. (定数大きめ)

(ところで, 最近問題を解くときにどうでもいい弊害(bccのscanfで文字列を取るときに32767文字しか読み取らないとか)で時間を吸われていて残念です.)

コード :

#include <cstdio>
#include <cstring>

#define MOD (10000000)

using namespace std;

int w, m;
int dp[300010][27];
bool ng[27][27];
char str[300010];

int main()
{
	
	scanf("%d", &w);
	scanf("%s", &str[1]);
	scanf("%d", &m);
	
	for (int i = 0; i < m; i++){
		char s[8], t[8];
		scanf("%s %s", s, t);
		s[0] -= 'A'; t[0] -= 'A';
		ng[s[0]][t[0]] = true;
	}
	
	for (int i = 1; i <= w; i++) str[i] -= 'A';
	
	memset(dp, 0, sizeof(dp));
	
	dp[0][26] = 1;
	for (int i = 1; i <= w; i++){
		memcpy(dp[i], dp[i - 1], sizeof(dp[i - 1]));
		int next = 0;
		for (int j = 0; j <= 26; j++){
			if (!ng[j][str[i]]){
				next += dp[i - 1][j];
				next %= MOD;
			}
		}
		dp[i][str[i]] = next;
	}
	
	int ans = 0;
	for (int i = 0; i < 26; i++) ans = (ans + dp[w][i]) % MOD;
	
	printf("%d\n", ans);
	
	return (0);
}