「第20回オフラインリアルタイムどう書く」の解答(改善?編)

前回の続きです。

finalfusionさんから次のアイデアをいただきました。

なるほど、たしかに。ありがとうございます。

というわけで、ジャマ川さんもとい座間川さんの予定だけ、trueとfalseを反転させようと考えました。

そのためには、配列の要素をすべて走査しなければなりません。……それはなんとなくイヤです。

配列をListに変換すれば、なんらかの便利メソッドが使えそうな気がします。しかし、配列はオートボクシングされないので、最初からBooleanの配列を使う必要があります。

でも"なんらかの便利メソッド"って何でしょう? booleanないしBooleanでは値が2種類しかありませんので、値の反転は面倒です(片方の値を退避できないため)。

諦めてビット使えや感が増してきましたが、それは無視して、方針を変えることにしました。やはりbooleanの配列がダメだったのです。そうだ、文字列を使おう。

こうなりました。

import java.util.*;

public class Meetime {

	private static final char FREE = 'f';
	private static final char BUSY = 'b';

	public String solve(String input) {
		Map<Character, String> schedule = parse(input);
		List<Integer> meeting = new ArrayList<>();

		meeting = check(schedule.get('A'), schedule.get('B'),
				schedule.get('I'), schedule.get('Z'), meeting);
		meeting = check(schedule.get('A'), schedule.get('B'),
				schedule.get('J'), schedule.get('Z'), meeting);

		Collections.sort(meeting);

		if (0 < meeting.size()) {
			return convertToTime(meeting.get(0)) + "-"
					+ convertToTime(meeting.get(0) + 60);
		}
		return "-";
	}

	List<Integer> check(String as, String bs, String xs, String zs,
			List<Integer> meeting) {
		int beginIndex = 0;
		int tmp = 0;
		for (int i = 60 * 10; i < 60 * 18; i++) {
			if (tmp == 0) {
				beginIndex = i;
			}
			if (as.charAt(i) == FREE && bs.charAt(i) == FREE
					&& xs.charAt(i) == FREE && zs.charAt(i) == BUSY) {
				tmp++;
			} else {
				tmp = 0;
				beginIndex = 0;
			}
			if (59 < tmp) {
				meeting.add(beginIndex);
				tmp = 0;
				beginIndex = 0;
			}
		}
		return meeting;
	}

	Map<Character, String> initSchedule() {
		Map<Character, String> result = new HashMap<>();
		result.put('A', new String(new char[1440]).replace('\0', FREE));
		result.put('B', new String(new char[1440]).replace('\0', FREE));
		result.put('I', new String(new char[1440]).replace('\0', FREE));
		result.put('J', new String(new char[1440]).replace('\0', FREE));
		result.put('Z', new String(new char[1440]).replace('\0', FREE));
		return result;
	}

	Map<Character, String> parse(String input) {
		Map<Character, String> result = initSchedule();
		String[] split = input.split(",");
		for (String str : split) {
			char name = str.charAt(0);
			String schedule = result.get(name);
			schedule = fill(schedule, convertToIndex(str.substring(1, 5)),
					convertToIndex(str.substring(6, 10)));
			result.put(name, schedule);
		}
		return result;
	}

	String fill(String schedule, int begin, int end) {
		StringBuilder sb = new StringBuilder(schedule);
		StringBuilder replaced = sb.replace(begin, end, new String(new char[end
				- begin]).replace('\0', BUSY));
		return replaced.toString();
	}

	String convertToTime(int index) {
		int hour = index / 60;
		int minute = index % 60;
		return String.format("%02d", hour) + String.format("%02d", minute);
	}

	int convertToIndex(String time) {
		int hour = Integer.parseInt(time.substring(0, 2));
		int minute = Integer.parseInt(time.substring(2, 4));
		return hour * 60 + minute;
	}
}

Javaで「ある文字sをn個連結した文字列を作る」イディオムを探したところ、なるほどというアイデアを見つけたので、メモしておきます。

Here is the shortest version (Java 1.5+ required):

repeated = new String(new char[n]).replace("\0", s);

No imports or libraries needed.

Simple way to repeat a String in java - Stack Overflow

こりゃエレガントで素晴らしい方法だ!というレスがついててワロタです。これがJava

Java 8でStringにjoinが入ったんだから、この手のメソッドを入れてくれてもいいのにと思います。

あ、肝心のジャマ川さんの反転は、次のように書けばできますが、

		String zs = schedule.get('Z');
		zs.replaceAll("f", "x");
		zs.replaceAll("b", "f");
		zs.replaceAll("x", "b");

checkメソッド内の条件式が次のようになるだけになってしまったので、

			if (as.charAt(i) == FREE && bs.charAt(i) == FREE
					&& xs.charAt(i) == FREE && zs.charAt(i) == FREE)

変更は見送りましたw