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 C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;

class _0040 {
    static int Mod(int n, int k) {
        return n % k + ((n % k < 0) ? k : 0);
    }
    static int Inv(int a) {
        switch (a) {
            case 1: return 1;
            case 3: return 9;
            case 5: return 21;
            case 7: return 15;
            case 9: return 3;
            case 11: return 19;
            case 15: return 7;
            case 17: return 23;
            case 19: return 11;
            case 21: return 5;
            case 23: return 25;
            case 25: return 25;
            default: return 0;
        }
    }
    static void MakeTable(Dictionary<string, int[]> tiT, Dictionary<string, int[]> taT) {
        string ti = "this", ta = "that";
        int[] at = { 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25 };
        int b;
        char[] str = new char[4];
        int[] res;

        tiT.Clear(); taT.Clear();
        for (int i = 0; i < at.Length; i++) {
            for (b = 0; b < 26; b++) {
                res = new int[] { at[i], b, Inv(at[i]) };
                for (int j = 0; j < 4; j++)
                    str[j] = (char)('a' + Mod(at[i] * (ti[j] - 'a') + b, 26));
                tiT.Add(new string(str), res);
                for (int j = 0; j < 4; j++)
                    str[j] = (char)('a' + Mod(at[i] * (ta[j] - 'a') + b, 26));
                taT.Add(new string(str), res);
            }
        }
    }  
    public static void Main() {
        string input;
        string[] cand;
        Dictionary<string, int[]> tiT = new Dictionary<string, int[]>();
        Dictionary<string, int[]> taT = new Dictionary<string, int[]>();
        int[] set = new int[1];
        char[] op;

        MakeTable(tiT, taT);
        int n = int.Parse(Console.ReadLine());
        for(int i = 0; i < n; i++) {
            input = Console.ReadLine();
            op = new char[input.Length];
            cand = Array.FindAll(input.Split(), str => str.Length == 4);
            for (int j = 0; j < cand.Length; j++) {
                if (tiT.ContainsKey(cand[j])) {
                    set = tiT[cand[j]];
                    break;
                } else if (taT.ContainsKey(cand[j])) {
                    set = taT[cand[j]];
                    break;
                }
            }
            for (int j = 0; j < input.Length; j++)
                if ('a' <= input[j] && input[j] <= 'z')
                    op[j] = (char)('a' + Mod(set[2] * ((input[j] - 'a') - set[1]), 26));
                else
                    op[j] = input[j];
            Console.WriteLine(new string(op));
        }
    }
}