AOJ2177 Champernowne Constant

問題リンク Champernowne Constant

  • 概要

Champernowne定数とは、"0."の後ろに正の数を連続で並べたような無理数の事である。
0.1234567891011121314...
2つの正の整数NとKが与えられる。Champernowne定数の小数点以下N位からK文字を求めよ。
1 <= N <= 10^9
1 <= K <= 100

  • 解法

区間分割で解きました。
10億以上の桁を生成するのは当然無理なので、1からの整数を1000個ずつ分割して考えます。
区間 i (0<=i)にはi*1000からi*1000+999までの整数があることになります。
ここで、区間の両端の整数i*1000とi*1000+999の桁数が等しかったら、この区間の長さは1000*桁数とすぐ分かります。もし違ったら、実際に1000個の整数の桁数を加算すればいいでしょう。区間の両端の数字が異なる箇所は多くても10回程度しか起こらないので、ほとんどの区間で定数時間で求まることになります。
配列a[i]には区間iより前にある区間の桁数の総和が入っているものとします。このとき、出力するべき部分の先頭を含んでいる区間iは
a[i] <= N && N < a[i+1]
を満たす場所となります。これを二分探索なり、めんどくさかったら線形時間なりで求めます。
目的の区間iが求まったら、N〜N+K-1文字目を覆うまで、文字列を作って出力すればOKです。

  • ソース
import java.util.Scanner;

//Champernowne Constant
public class AOJ2177 {

	void run(){
		int S = 125000;
		int[] a = new int[S];
		int s = 0;
		for(int i=0;i<S;i++){
			a[i] = s;
			int L = 1000*i, R = L+999;
			if((L+"").length()==(R+"").length())s+=1000*(L+"").length();
			else{
				for(int k=L;k<=R;k++)s+=(k+"").length();
			}
		}
		Scanner sc = new Scanner(System.in);
		for(;;){
			int N = sc.nextInt(), K = sc.nextInt();
			if((N|K)==0)break;
			int id = 0;
			for(int i=0;i+1<S;i++){
				if(a[i]<=N&&N<a[i+1]){
					id = i; break;
				}
			}
			N-=a[id];
			StringBuilder sb = new StringBuilder();
			for(int k=1000*id;sb.length()<=N+K;k++)sb.append(k);
			System.out.println(sb.substring(N, N+K));
		}
	}
	
	public static void main(String[] args) {
		new AOJ2177().run();
	}
}