Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

アフィン暗号

簡単な暗号法の一つに、アフィン暗号というものがあります。まず、アルファベット a〜z を a = 0, b = 1, c = 2,..., x = 23, y = 24, z = 25 と 0〜25 の数字に置き換えます。そして、以下の式で、原文のアルファベットを置換します。

$F(\gamma) = (\alpha \cdot \gamma + \beta)$ mod $26$

ただし、mod 26 は 26 で割った余りを表します。例えば、$\alpha = 3, \beta = 2$ のとき、アルファベットの 'a' (=0) は、$F(0) = (3 \cdot 0 + 2)$ mod $26 = 2$ で 'c' に、アルファベットの 'n' (=13) は $F(13) = (3 \cdot 13 + 2)$ mod $26 = 15$ で 'p' に置換されます。
このとき、$\gamma$ に対する $F(\gamma)$ が必ず 1 対 1 で対応付けられるように、$\alpha$ と $\beta$ は慎重に選ばれているものとします（$\alpha$ と 26 が互いに素であることが条件）。$\alpha = 4, \beta = 7$ のときのように、$F('a') = 7, F('n') = 7$ と、'a' も 'n' も同じ 'h' に置換されるようなことはありません。また、アルファベット以外の文字は置換されません。

暗号化された文字列を元の文章に復号したものを出力するプログラムを作成してください。元の文章には、キーワードとして

that
this

のいずれかが必ず含まれているものとします。

Input

複数のデータセットが与えられます。１行目にデータセット数 $n$ ($n \leq 30$) が与えられます。続いて $n$ 行のデータが与えられます。各データセットに英小文字と空白からなる 256 文字以内の暗号化された文章が１行に与えられます。

Output

各データセットに対して、復号した元の文章を１行に出力して下さい。

Sample Input

1
y eazqyp pnop pngtg ye obmpngt xmybp mr lygw

Output for the Sample Input

i submit that there is another point of view

Solution in JAVA (judge verdict: Accepted)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

import static java.lang.Integer.parseInt;

/**
 * Affine Cipher
 */
public class Main {

	static final char[][] hints = {{'t', 'h', 'i', 's'}, {'t', 'h', 'a', 't'}};

	public static void main(String[] args) throws IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = parseInt(br.readLine());

		for (int i = 0; i < n; i++) {
			String s = br.readLine();
			int[] c = cipher(s);
			int[] d = new int[26];
			for (int j = 0; j < 26; j++) {
				d[(c[0] * j + c[1]) % 26] = j;
			}
			StringBuilder sb = new StringBuilder();
			for (int j = 0; j < s.length(); j++) {
				if (s.charAt(j) == ' ') {
					sb.append(' ');
				} else {
					sb.append((char) (d[s.charAt(j) - 'a'] + 'a'));
				}
			}
			System.out.println(sb.toString());
		}
	}

	static int[] cipher(String s) {

		int[] ret = new int[2];

		StringTokenizer st = new StringTokenizer(s);

		while (st.hasMoreTokens()) {

			String w = st.nextToken();

			if (w.length() == 4) {

				char[] hint;

				if (w.charAt(0) != w.charAt(w.length() - 1)) {
					hint = hints[0];
				} else {
					hint = hints[1];
				}

				for (int a = 0; a < 26; a++) {
					if (a % 2 != 0 && a % 13 != 0) {
						for (int b = 0; b < 26; b++) {
							StringBuilder h = new StringBuilder();
							for (int i = 0; i < hint.length; i++) {
								h.append((char) (((a * (hint[i] - 'a') + b) % 26) + 'a'));
							}
							if (w.equals(h.toString())) {
								ret[0] = a;
								ret[1] = b;
								return ret;
							}
						}
					}
				}
			}
		}

		return ret;
	}
}