B - Password
時間制限 : 2sec / スタック制限 : 64MB / メモリ制限 : 64MB問題文
セキュリティに興味がある高橋君は、デジタルアーツ株式会社に就職したい青年です。そこで、高橋君は自分が運営するサービスであるAtCoderのセキュリティについて見なおしてみることにしました。
現在AtCoderのシステムでは、パスワードは 1 文字以上 20 文字以下の英小文字のみとしています。
そして、文字列 s に対してハッシュ値( hash(s) )を求める以下の式があり、
パスワードと入力した文字列に対して、それぞれこの式で算出したハッシュ値が一致すると、
入力した文字列は正しいとみなします。
hash(s)=Num(c1)+Num(c2)+…+Num(cN) (ci は文字列 s の i 番目の文字を意味する)
なお、上記の式の関数 Num() とは英小文字を数字に変換する関数で、
Num(a)=1, Num(b)=2, …., Num(z)=26 というように、
a から z に対して順に 1 から 26 までの数字を返します。
高橋君は、このシステムではパスワードと違う文字列でも
簡単にハッシュ値が一致してしまうことに気づきました。
例えば、文字列 abc のハッシュ値は、1+2+3=6 となりますが、
文字列 bbb のハッシュ値も 2+2+2=6 ですし、f も 6 になってしまいます。
高橋君は、現在使っているパスワードに対してどのような文字列が
正しいパスワードとして認識されてしまうか知りたいです。
正しいパスワード以外で条件を満たすものを 1 つ出力しなさい。
条件を満たすものが複数ある場合は、どの文字列を出力しても構いません。
もし条件を満たすパスワードが存在しない場合は NO と出力しなさい。
なお、AtCoderのシステムで入力できるパスワードは 1 文字以上 20 文字以下の英小文字のみなので、
答えとして出力する文字列もその条件をみたします。
入力
入力は以下の形式で標準入力から与えられる。c1c2‥‥cN入力には正しいパスワードを表す長さ N(1≦N≦20) の文字列が 1 行で与えられる。
正しいパスワードの i 番目の文字を表す ci は英小文字 (a-z) である。
出力
与えられた正しいパスワードを表す文字列と等しいハッシュ値になる英小文字 1 文字以上 20 文字以下の文字列を、正しいパスワード以外のいずれか 1 つ出力せよ。
また、そのような文字列が存在しない場合は NO と出力せよ。
なお、出力は 1 行のみとし、最後には改行を出力せよ。
出典
B: Password - DigitalArts プログラミングコンテスト2012 | AtCoder回答
AtCoder/digitalarts_2.php at master · wada811/AtCoder · GitHub<?php $password = trim(fgets(STDIN)); $ascii_a = ord('a') - 1; $numbars = array(); for($i = 0, $end = strlen($password); $i < $end; $i++){ $numbars[] = ord($password[$i]) - $ascii_a; } $sum = array_sum($numbars); // special case if($sum === 1 || $sum === 26 * 20){ // 'a' or 'zzzzzzzzzzzzzzzzzzzz' echo 'NO' . PHP_EOL; exit(); } $answers = array(); if(count($numbars) === 1){ // 'b' - 'z' $answers[0] = $numbars[0] - 1; $answers[1] = 1; }else{ while($sum){ $answers[] = min(26, $sum); $sum -= min(26, $sum); } } // ex. 'za' -> 'az' while($answers == $numbars){ shuffle($answers); } $another_password = ''; for($i = 0, $count = count($answers); $i < $count; $i++){ $another_password .= chr($answers[$i] + $ascii_a); } echo $another_password . PHP_EOL; ?>2行目でtrimし忘れて意図しない改行コードで答えが合わなくて泣きそうになった。
文字列を配列でアクセスできるので1文字ずつアスキーコード変換して配列に入れる。
答えが NO になる例外を弾き、1文字のパスワードとそうでないもので処理を分ける。
後は28行目に書いたようなやつのためにshuffleして入れ替えておく。コレで確実。
あとはアスキーコードを逆変換して出力!