すでに記憶が薄れつつありますが思い出しながら書いてみます。
Google の AppEngine にdeploy しましたが、とりあえず数字列から漢数字に変換する部分だけ載せておきます。確認しやすいように、GET のパラメータによって回答用のASCII文字と漢数字を切り替えられるようにしているので最後の方はどうでもいいです。
コードは以下のような感じで。実際はこのファイルにAppEngineのサーブレット用のクラスとUT用のクラスを作りました。
package org.zakky.devfest2010.kansuuji;
import java.util.Arrays;
import java.util.LinkedList;
import com.google.appengine.repackaged.com.google.common.collect.ImmutableBiMap;
import com.google.appengine.repackaged.com.google.common.collect.ImmutableList;
import com.google.appengine.repackaged.com.google.common.collect.ImmutableMap;
import com.google.appengine.repackaged.com.google.common.collect.Iterables;
import com.google.appengine.repackaged.com.google.common.collect.Lists;
public class KansuujiSolver {
private static final String ZERO = "N";
private static final String ONE = "Q";
private static final String TWO = "R";
private static final String THREE = "D";
private static final String FOUR = "B";
private static final String FIVE = "X";
private static final String SIX = "L";
private static final String SEVEN = "A";
private static final String EIGHT = "Z";
private static final String NINE = "E";
private static final String JUU = "Y";
private static final String HYAKU = "J";
private static final String SEN = "C";
private static final String MAN = "G";
private static final String OKU = "H";
private static final String CHOU = "S";
// 数字の変換マップ。0は出力する必要がないので空文字列にします
private static ImmutableMap<Character, String> digitMap__;
static {
final String digits = "123456789";
final String ascii = "" + ONE + TWO + THREE + FOUR + FIVE + SIX + SEVEN
+ EIGHT + NINE;
assert digits.length() == ascii.length();
final ImmutableMap.Builder<Character, String> builder;
builder = ImmutableMap.builder();
builder.put('0', "");
for (int index = 0; index < digits.length(); index++) {
builder.put(digits.charAt(index), "" + ascii.charAt(index));
}
digitMap__ = builder.build();
}
// 4桁ごとに繰り返される位のための文字("", "十"、"百"、"千")。
private static ImmutableList<String> BASE_UNITS = ImmutableList
.copyOf(Arrays.asList("", JUU, HYAKU, SEN));
// 4桁増えるごとに付加される位のための文字列("", "万"、"億"、"兆")。
private static ImmutableList<String> EXTRA_UNITS = ImmutableList
.copyOf(Arrays.asList("", MAN, OKU, CHOU));
private final String input_;
private final Translator translator_;
public KansuujiSolver(String input) {
this(input, false);
}
public KansuujiSolver(String input, boolean asKanji) {
input_ = input;
translator_ = asKanji ? k : a;
if (!isValidInput(input)) {
throw new IllegalArgumentException("illegal argument: " + input);
}
}
private static boolean isValidInput(String input) {
if (input.isEmpty()) {
return false;
}
if (KansuujiSolver.getMaxDigits() < input.length()) {
return false;
}
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (c < '0' || '9' < c) {
return false;
}
}
return true;
}
private static int getMaxDigits() {
return BASE_UNITS.size() * EXTRA_UNITS.size();
}
public String getAnswer() {
final StringBuilder sb = new StringBuilder(); // 逆順に結果を保持する
if (isAllZero(input_)) {
sb.append(ZERO);
return getAnswer(sb, translator_);
}
final LinkedList<Character> string = Lists.newLinkedList();
for (char c : input_.toCharArray()) {
string.add(Character.valueOf(c));
}
final Iterable<Character> reverseString = Iterables.reverse(string);
int count = 0;
boolean blockEmpty = true;
for (Character d : reverseString) {
final int baseMod = count % BASE_UNITS.size();
if (baseMod == 0) {
sb.append(EXTRA_UNITS.get(count / BASE_UNITS.size()));// 万、億、兆
blockEmpty = true; // 4桁ごとのブロックの始まりなのでリセット
}
if (d != '0') {
sb.append(BASE_UNITS.get(baseMod)); // 十、百、千
blockEmpty = false;
}
if (d != '1' || baseMod == 0) { // 1, 5, 9, 15桁目が1ではない
final String dStr = digitMap__.get(d);
if (!dStr.isEmpty()) {
sb.append(dStr);
blockEmpty = false;
}
}
if (baseMod == BASE_UNITS.size() - 1 && blockEmpty) {
final String extraUnitStr = EXTRA_UNITS.get(count
/ BASE_UNITS.size());
if (!extraUnitStr.isEmpty()) {
// 1億万1対策
sb.delete(sb.length() - extraUnitStr.length(), sb.length());
}
}
count++;
}
return getAnswer(sb, translator_);
}
private static boolean isAllZero(String input) {
for (int i = 0; i < input.length(); i++) {
if (input.charAt(i) != '0') {
return false;
}
}
return true;
}
private static String getAnswer(StringBuilder sb, Translator t) {
final StringBuilder answer = new StringBuilder(sb.length());
// 変換をかけつつ反転
for (int index = sb.length() - 1; 0 <= index; index--) {
answer.append(t.convert(sb.charAt(index)));
}
return answer.toString();
}
private interface Translator {
public char convert(char in);
}
private static final Translator a = new Translator() {
public char convert(char in) {
return in;
}
};
private static final Translator k = new Translator() {
private final ImmutableBiMap<Character, Character> a2k;
{
final String ascii = "" + ZERO + ONE + TWO + THREE + FOUR + FIVE
+ SIX + SEVEN + EIGHT + NINE + JUU + HYAKU + SEN + MAN
+ OKU + CHOU;
final String kanji = "零一二三四五六七八九十百千万億兆";
assert ascii.length() == kanji.length();
final ImmutableBiMap.Builder<Character, Character> builder;
builder = ImmutableBiMap.builder();
for (int index = 0; index < ascii.length(); index++) {
builder.put(ascii.charAt(index), kanji.charAt(index));
}
a2k = builder.build();
}
public char convert(char in) {
return a2k.containsKey(in) ? a2k.get(in) : in;
}
};
}