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

// 標準入力
var fs = require('fs'),
length = fs.fstatSync(process.stdin.fd).size,
buffer = new Buffer(length),
bytesRead = fs.readSync(process.stdin.fd, buffer, 0, length, 0),
input = buffer.toString('hex', 0, bytesRead).split('0a'); // hexでreadしたら早い？
main();
 
function main(){
  // 26と互いに素の値
  var multiply = new Int8Array([1,3,5,7,9,11,15,17,19,21,23,25]);
  var lenj = multiply.length;
  var alphabets = new Int8Array(26);
  var convertTable = new Int8Array(123);
  for(var init = 123; init--;)
    convertTable[init] = init;

FIRST:for(var i = 1, leni = input.length - 1; i < leni; i++){
    var inp = input[i];
    var c = inp.split('20');
    var clen = c.length;
  
    for(var j = 0; j < lenj; j++){
      var alpha = multiply[j];
      for(var beta = 26; beta--;){
        for(var conv = 26; conv--;){ // a-zの変換表作成
          alphabets[conv] = (conv * alpha + beta) % 26 + 97;
        }

        // thatとthisの変換後のacsiiコードを取得
        var thatIs = alphabets[19].toString(16) + alphabets[7].toString(16) + alphabets[0].toString(16) + alphabets[19].toString(16);
        var thisIs = alphabets[19].toString(16) + alphabets[7].toString(16) + alphabets[8].toString(16) + alphabets[18].toString(16);

        var found = false;
        for(var d = clen; d--;){
          var cd = c[d];
          if(cd == thatIs || cd == thisIs){
            found = true;
            break;
          }
        }
        
        if(found){
          for(var conv = 26; conv--;){
            var fromCode = alphabets[conv];
            convertTable[fromCode] = conv + 97; // key-valueの入れ替え
          }
          
          var result = "";
          for(var k = 0; k < inp.length; k+=2){
            result += String.fromCharCode(convertTable[parseInt(inp[k] + inp[k+1], 16)]);
          }
          console.log(result);
          continue FIRST;
        }
        
      }
    }
  
  }
}