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.Collections.Generic;
using System;

public class Check
{
    public bool chk { get; set; }
    public int a { get; set; }
    public int b { get; set; }
}

public class Hello
{
    public static void Main()
    {
        var alp = "abcdefghijklmnopqrstuvwxyz";
        var d = new Dictionary<char, int>();
        var d2 = new Dictionary<int, char>();
        for (int i = 0; i < 26; i++) { d[alp[i]] = i; d2[i] = alp[i]; }
        var n = int.Parse(Console.ReadLine().Trim());
        for (int i = 0; i < n; i++)
        {
            string ss = Console.ReadLine().Trim();
            string[] s = ss.Split(' ');
            for (int j = 0; j < s.Length; j++)
            {
                if (s[j].Length == 4 && s[j][0] == s[j][3])
                {
                    var that = checkThat(s[j], d);
                    if (that.chk) { changeAll(ss, d, d2, that.a, that.b); goto end; }
                }
                if (s[j].Length == 4 && s[j][0] != s[j][3])
                {
                    var This = checkThis(s[j], d);
                    if (This.chk) { changeAll(ss, d, d2, This.a, This.b); goto end; }
                }
            }
            end:;
        }
    }
    public static void changeAll(string s, Dictionary<char, int> d, Dictionary<int, char> d2, int a, int b)
    {
        for (int i = 0; i < s.Length; i++)
        {
            if (s[i].ToString() == " ") Console.Write(" ");
            else Console.Write(changeChr(s[i].ToString(), d, d2, a, b));
        }
        Console.WriteLine();
    }
    public static string changeChr(string s, Dictionary<char, int> d, Dictionary<int, char> d2, int a, int b)
    {
        for (int i = 0; i < 26; i++)
            if ((a * i + b) % 26 == d[s[0]]) return d2[i].ToString();
        return "";
    }
    public static Check checkThis(string s, Dictionary<char, int> d)
    {
        var ret = new Check();
        var tchange = d[s[0]]; var hchange = d[s[1]]; var ichange = d[s[2]]; var schange = d[s[3]];
        for (int i = 0; i < 26; i++)
        {
            for (int j = 0; j < 26; j++)
            {
                if (((d['t'] * i + j) % 26 == tchange) &&
                        ((d['h'] * i + j) % 26 == hchange) &&
                     ((d['i'] * i + j) % 26 == ichange) &&
                     ((d['s'] * i + j) % 26 == schange)) { ret.a = i; ret.b = j; ret.chk = true; return ret; }
            }
        }
        ret.chk = false; return ret;
    }
    public static Check checkThat(string s, Dictionary<char, int> d)
    {
        var ret = new Check();
        var b = d[s[2]];
        var tchange = d[s[0]];
        var hchange = d[s[1]];
        for (int i = 0; i < 26; i++)
        {
            if (((d['t'] * i + b) % 26 == tchange) &&
                    ((d['h'] * i + b) % 26 == hchange)) { ret.chk = true; ret.a = i; ret.b = b; return ret; }
        }
        ret.chk = false; return ret;
    }
}