AOJ2209 UTF-8

問題リンク UTF-8

  • 解法

DPです。
dp[i][j]: iバイト目をjバイト文字の先頭と解釈したときのiバイト目以降の解釈の組み合わせ数
という表を後ろのバイトから埋めていけば解けます。
iバイト目をjバイト文字と解釈する方法を説明します。
まず、マスクが0や1のときに入力がその文字でなかったらjバイト文字を作れないので0となります。
変数xsを、マスクがxのときに入力もxだったときの個数。ysを、マスクがyのときに入力がxだったときの個数。y1をマスクがyだったときに入力が1だったときの個数とします。
解釈しようとする文字が1バイト文字だった場合はマスクにyが登場しないので、解釈の仕方は 2^xs 通りとなります。
2,3,4バイト文字だった場合で
y1が0のとき、マスクがyの部分は (2^ys)-1 通りの解釈ができます。-1してるのは、オール0の場合を抜かすためです。
y1が0でないとき、yの箇所に1が少なくとも1つ現れるという条件をすでにクリアしているので、マスクがyの部分は 2^ys 通りの解釈ができます。
それぞれに対して、マスクxの部分は 2^xs 通りの解釈ができるので、結局
(2^xs)*((2^ys)-1) 通り
(2^xs)*(2^ys) 通り
の解釈ができます。

  • ソース
import java.util.Scanner;

//UTF-8
public class AOJ2209 {

	String[][] p = {{"0xxxxxxx"}, 
			{"110yyyyx", "10xxxxxx"},
			{"1110yyyy", "10yxxxxx", "10xxxxxx"},
			{"11110yyy", "10yyxxxx", "10xxxxxx", "10xxxxxx"}
	};

	void run(){
		Scanner sc = new Scanner(System.in);
		int MOD = 1000000;
		for(;;){
			int n = sc.nextInt();
			if(n==0)break;
			char[][] s = new char[n][8];
			for(int i=0;i<n;i++)s[i]=sc.next().toCharArray();
			long[][] dp = new long[n+1][4];
			dp[n][0] = 1;
			for(int i=n-1;i>=0;i--)for(int L=0;L<4;L++){
				if(n<=i+L)continue;
				boolean ok = true;
				int ys = 0, xs = 0, y1 = 0;
				for(int j=0;j<=L;j++)for(int k=0;k<8;k++){
					char ch = p[L][j].charAt(k);
					if(ch=='0'){
						if(s[i+j][k]=='1')ok = false;
					}
					if(ch=='1'){
						if(s[i+j][k]=='0')ok = false;
					}
					if(ch=='x'){
						if(s[i+j][k]=='x')xs++;
					}
					if(ch=='y'){
						if(s[i+j][k]=='1')y1++;
						if(s[i+j][k]=='x')ys++;
					}
				}
				if(ok){
					long res = (1L<<xs)%MOD;
					if(0<L){
						if(y1==0){
							res = (res*((1<<ys)-1))%MOD;
						}
						else{
							res = (res*(1<<ys))%MOD;
						}
					}
					for(int j=0;j<4;j++){
						dp[i][L] = (dp[i][L]+res*dp[i+L+1][j])%MOD;
					}
				}
				else dp[i][L] = 0;
			}
			long res = 0;
			for(int j=0;j<4;j++)res+=dp[0][j];
			System.out.println(res%MOD);
		}
	}

	public static void main(String[] args) {
		new AOJ2209().run();
	}
}