Re: Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)
- 2011.11.04 Friday
- dev
JunichiIto さんによる「Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)」という記事を見かけて自分でもやってみる。
正確に時間を測ったわけではありませんが、問題 1 と問題 2 あわせて一応 90 分で解けたと思います。
- 問題1: Excel列名変換問題
- 仕様
- 入力されたアルファベットを数字に変換する。
- 変換ルールはExcelの列名と同等。
- 例) A=1、B=2、Z=26、AA=27、XFD=16384
- 起動時引数
- [0] アルファベット (A〜ZZZZ...[上限なし])
- 実行例
- ExcelColConv.pl A → 1
- ExcelColConv.pl AA → 27
[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]
- 問題2: Excel列名変換問題(逆変換)
- 仕様
- 入力された数字をアルファベットに変換する。
- ただし、問題1で作ったプログラムを拡張すること。
- 起動時引数
- [0] 0=数字へ変換、1=アルファベットへ変換
- [1] 変換する数字またはアルファベット(どちらも上限なし)
[→Excel列名変換問題で第2回社内プログラミングコンテストを開催してみた(前編)]
問題 1 回答
#!/bin/perl if ($ARGV[0] =~ /[A-Za-z]+/) { print &convert(uc($ARGV[0])) . "\n"; } else { &usage(); } exit 0; sub usage { print 'Usage: ',$0,' input',"\n"; } sub convert { my $input = shift; my $output = 0; my $rank = 0; foreach my $char ( reverse(split(//,$input)) ) { my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1; $output += $num * (26 ** $rank++); } return $output; }
アプローチとしては、3 位の方と同等ですね。一応、入力パラメータチェックしてます。
問題 2 回答
#!/bin/perl if ($ARGV[0] eq '0' and $ARGV[1] =~ /[A-Za-z]+/) { print &convert(uc($ARGV[1])) . "\n"; } elsif ($ARGV[0] eq '1' and $ARGV[1] =~ /[0-9]+/ and $ARGV[1] > 0) { print &rev_convert($ARGV[1]) . "\n"; } else { &usage(); } exit 0; sub usage { print 'Usage: ',$0,' dir input',"\n"; print ' dir 0 : alpha to num [input must be alphabet]'."\n"; print ' dir 1 : num to alpha [input must be number greater than 0]'."\n"; } sub convert { my $input = shift; my $output = 0; my $rank = 0; foreach my $char ( reverse(split(//,$input)) ) { my $num = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $char) + 1; $output += $num * (26 ** $rank++); } return $output; } sub rev_convert { my $input = shift; my $output = ''; my @chars = split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); my $base = @chars; my @stack = (); while ($input > $base) { my $num = $input % $base; push(@stack, $chars[$num - 1]); $input = int($input / $base); # Needs to decrement $input if $input value is in multiple of $base if ($num == 0) { $input--; } } # Now handles within $base case push(@stack, $chars[$input % $base - 1]); return join('',reverse(@stack)); }
入力数値が基数にあたる 26 の倍数の時、桁を正しく処理する必要があるのがトリッキーな部分になるでしょうか。
一応、ざっくり確認したつもりですが、間違っていたらごめんなさい。
[2010.11.04 03:30 追記] perl の場合、配列に対して負のインデックスを指定すると、配列の後ろからアクセスするという仕様があります。なので、$input % $base
の結果が 0 になる場合、@stack
には "Z" が格納されます。
スポンサーリンク