問題文を理解しましょう
問題文を理解しましょう
私が子供の頃は、まだ1ドル=360円の時代でした。なので、こんななぞなぞがありました。
問『ハンドルは何円?』
答『180円』(半ドルだから)
現在は相場が変動するのでリアルタイムに調べないと答えられませんが😂、当時は固定だったのです。
問題文に『ドルを示す正整数 N が与えられるので、N に 360 を掛けた値を出力してください。』とあります。1ドル=360円と定義されていますので、Nドル = N×360円です。
ヒントを頼りにプログラムを組んでみてください。
私が子供の頃は、まだ1ドル=360円の時代でした。なので、こんななぞなぞがありました。
問『ハンドルは何円?』
答『180円』(半ドルだから)
現在は相場が変動するのでリアルタイムに調べないと答えられませんが😂、当時は固定だったのです。
問題文に『ドルを示す正整数 N が与えられるので、N に 360 を掛けた値を出力してください。』とあります。1ドル=360円と定義されていますので、Nドル = N×360円です。
真っ先に 文を思い浮かべるのは自然なことです。しかし方法は1つしかないなんてことはありえません。
Python3 では、こんな機能があったことを覚えていますか?
表を作成するときに、ラインを引いたりする際に使ったことがあるかもしれません。あとはこれを使ってどうにかすれば、 文を使わない方法で結果を実現させることができるはずです。
文字列に含まれる 't' と 'a' を削除したいわけですが、大きく分けて、
それでは早速アレしてみましょう。😊
「取り除く」といったら アレ でしょう。ただし取り除けるのは1回につき1つのみです。さらに該当文字が無ければエラーが返されてしまいます。
't' と 'a' の文字数を予めカウントしておき、それぞれの回数分アレして取り除くとかしちめんどくさいなもう!
やってみてください。😊
もう1つ、取り除くといったら コレ もアリでしょう。
要素番号を調べる際、 .index() メソッドをそのまま使ってしまうと、やがてエラーとなります。どうしましょ?
めんどくさいからやっぱりナシだけど、やってみてください。
単純に先頭である要素0 から順に 't' と 'a' を探して該当要素を取り除く方法です。この方法だとエラーが起こりにくいです。
要素番号を使って取り除く方法全てで使えますね。一回の探索で済みますが、効率良いとはちょっと言い難いかな?1つ要素を取り除くと、それ以降の要素が全て前に詰められる処理が発生するからです。
要素番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
S | m | e | t | d | i | t | a | u | m |
↓ 要素番号 2 の 't' を取り除く
要素番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
S | m | e | d | i | t | a | u | m |
↓ 'd' を前に詰める
要素番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
S | m | e | d | i | t | a | u | m |
↓ 'i' を前に詰める
要素番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
S | m | e | d | i | t | a | u | m |
↓ (以下略)
要素番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
S | m | e | d | i | t | a | u | m |
前に詰める処理が6回発生しました。この処理は、文字列が長いほど、そして 't' や 'a' が多いほど時間がかかってしまいます。
除去する度に前に詰めていく処理の無駄を軽減するために、't' と 'a' の所を一時、''
(ヌルストリング) に置き換えておくのはどうでしょう?
['m', 'e', 't', 'd', 't', 'i', 'a', 'u', 'm']
↓
['m', 'e', '', 'd', '', 'i', '', 'u', 'm']
このリストを ''.join()
で文字列に変換すると、
medium
となります。いいね。👍
文字列には、文字を取り除くための関数やメソッドがありません。考え方を変えて、あるものをないものに変えてしまいましょう。
このメソッドは指定した文字を一気に変換してくれるのですが、't' と 'a' のように、複数の文字列を一度に指定することが出来ません。ですので、't' の分と 'a' の分、メソッドを計2回実行する必要があります。
't' と 'a' の2回分だけならまだしも、これが10文字、100文字と増殖していく度に回数分のメソッドを書いていくこととなってしまいます。文の出番です。
あるものをないものに変えてしまうという方法は変えずに、この機能を必要回数分繰り返せるようにします。単純に「あるものリスト」を作成して1つずつメソッドの「あるもの」に当てはめていくだけです。こうすれば文字列の変更や増減が自在になります。
【 Copilot が勝手に記述 】
リストに変換してから 't' と 'a' を取り除く方法もありますが、もう一つの方法は新しく文字列を作ることです。
具体的には、't' と 'a' 以外の文字を集めて新しい文字列を作るという方法です。
先に書かれた!🤣
線形探索で文字列の先頭から1文字ずつチェックしていき、't' または 'a' 以外の時、空の文字列で初期化した変数に代入していきます。't' と 'a' の時は無視します。
これを文字列の最後まで続ければ、最終的に taぬき の文字列が出来上がるというすんぽーなのだ。
格闘ゲームを知らない人には難題かと思われます。そんな方のために少し解説をしますが、昇龍拳やアキラ3連コンボの猛特訓をしていた過去がある方には不要と思われますので読み飛ばしちゃってください。
ゲームコントローラの十字ボタン(またはレバー)と、AボタンとBボタンがあるようです。ゲームコントローラすらわからない方は、
に現物画像がありますので、そちらで確認してください。
'L', 'R', 'U', 'D' は、それぞれ十字ボタンの「←」「→」「↑」「↓」でしょう。Aボタンが「パンチ」、Bボタンが「キック」と思われます。
rolling、 upper、 rush という3種類の技を作ります。
技名 | コマンド | 操作 |
---|---|---|
rolling | LLLRB | ←←←→B |
upper | DDRRA | ↓↓→→A |
rush | AAAAA | Aを5回 |
それぞれの技を使う為には、この通りにボタンを入力する必要があります。ピンと来ない方は、タイピングゲームのように、例えば 'DDRRA' とキーを打つと「upper」が使えると考えても差し支えありません。
条件を確認してみましょう。
文字列10万文字くらいなら強引なやり方をしてもイケちゃいそうです。出力する技名は、最低でも1つはあるということになります。何も技が出せなくて出力が空になるということはありません。
プログラムを組む上で最も考慮すべきところが、
『ただし、1 つのボタン入力は 1 つのコマンドにしか使用されません。例えば、"DDRRAAAAA" という入力があった場合、"DDRRA" はコマンド「upper」として認識しますが、残る入力は "AAAA" となるためコマンド「rush」は認識されないことに注意してください。』
この部分は次に説明します。
【入力 S】
DDRRAAAAALLLRB
問題集の入力例を使って説明します。
1
DDRRAAAAALLLRB
各技のコマンド入力数がすべて5ですので、5文字ずつ調べていきます。
DDRRAAAAALLLRB
まずここで何らかの技とコマンドが一致するかを調べます。
技名 | コマンド | 操作 |
---|---|---|
rolling | LLLRB | ←←←→B |
upper | DDRRA | ↓↓→→A |
rush | AAAAA | Aを5回 |
upper と一致しましたので、'upper'を画面に出力します。
upper
次に1つずつずらしてチェックしていきます。
DDRRAAAAALLLRB
↓
DDRRAAAAALLLRB
↓
DDRRAAAAALLLRB
↓
DDRRAAAAALLLRB
ここで 'rush' がヒットします。が、ここでの先頭の 'A' は既に最初の 'DDRRA' の末尾の 'A' で使用されています。
DDRRAAAAALLLRB
これが問題文の、
『残る入力は "AAAA" となるためコマンド「rush」は認識されないことに注意してください。』
の意味になります。
となると、単純に1つずつずらしてチェックしていく方法をとると 'AAAAA' がヒットし、'rush' が画面に出力されてしまいます。この現象はバグとなるため、これを解決できる別の方法を考える必要があります。
先頭からチェック済みの文字列を消去していきます。
DDRRAAAAALLLRB
これは 'upper' の技のコマンドとして成立します。ですので、5つの文字を消去します。
AAAALLLRB
また入力を調べていきます。
AAAALLLRB
↓
AAAALLLRB
↓先頭の1文字を消去
AAALLLRB
↓
AAALLLRB
↓先頭の1文字を消去
AALLLRB
↓
AALLLRB
↓先頭の1文字を消去
ALLLRB
↓
ALLLRB
↓先頭の1文字を消去
LLLRB
ここで 'rolling' の技のコマンドがヒットしました。技の発動と同時にコマンドを消去します。
LLLRB
↓
'' ← 文字列が空になった
・・・・・で、ここからどうしましょ?😅
探索終了条件をまだ決めていません。文字列が空になったら探索終了とすると、今回はたまたま最後にぴったり技のコマンドが出現して空になったから良いものの、最後までコマンドが出現しなかったり半端に残ってしまった場合は、空かどうかを終了条件にしてしまうと out of range エラーになってしまいそうですね。リアルタイムに入力しているのなら終了条件とか要りませんが、この問題の場合、ちと面倒そうです。
入力の文字列が4文字以下(または5文字でない)になったら探索を終了する。
→ 毎回 len()関数で数えるのは無駄なので、最初に文字数をカウントしてから都度文字数を引いていく。
現在位置からスライスで取得した文字列が4文字以下(5文字でない)になったら探索を終了する。
→ len()関数で毎回チェックする? え、するの?
ここまで書いておいてなんですが、私ならこの方法は選びません。
リアルタイムでコマンド入力して処理する場合は、不要となった入力は破棄してしまえばよいのですが、全ての入力から順に技をピックアップしていくとなると考え方を少し変えなければならないようです、たぶん。
もう、ゲームとかリアルタイムとか、そういうの全部無視して、この問題をラクにクリアするためだけの方法でやっつけちまいましょう。
技名 | コマンド | 操作 |
---|---|---|
rolling | LLLRB | ←←←→B |
upper | DDRRA | ↓↓→→A |
rush | AAAAA | Aを5回 |
DDRRAAAAALLLRB
問題文にあるとおり、技は3種類のみ。各種、技のコマンドも上記のように決まっています。先頭から該当する各コマンドを見つけ、別の文字に置き換えていきます。探索する技の順番に注意で、問題文にもあるように、'AAAAA' を最初に見つけてしまうと、'DDRRA' が見つけられなくなってしまう恐れがあります。上記の順番通りに探索してみましょう。
置き換える文字は「数字」にします。
1回目 'LLLRB'
DDRRAAAAALLLRB
↓
DDRRAAAAA0 ← 0 に置換えた
2回目 'DDRRA'
DDRRAAAAALLLRB
↓
1AAAA0 ← 1 に置換えた
3回目 'AAAAA'
1AAAA0
1つも見つからなかったので、そのまま
1AAAA0
これをどうするか。辞書を次のようにつくります。
dic = {
'0': 'rolling',
'1': 'upper',
'2': 'rush'
}
あとは文字列を線形探索で数字を見つけたら、キーとなっているその数字の値を画面に出力していきます。でもこの方法、線形探索を4回行うことになりますので、効率いいとは言えないですね。😓
今度は先頭から5文字ずつ、3つの技のいずれかのコマンドと一致するかを調べていきます。
dic = {
'LLLRB': 'rolling',
'DDRRA': 'upper',
'AAAAA': 'rush'
}
スライスなどで5文字の文字列を参照し、その文字列が辞書のキーに含まれているかを調べ、含まれていたらそのキーの値を画面に出力していきます。しかしこのままだとまた 'AAAAA' を見つけてしまって 'rush' が出力されてしまいますので、画面に出力したら現在位置を5文字分送ります。
そして文字列の末尾に到達したら探索を終了します。あっさりだ。😯
プログラム構造は次のとおりです。
S = (標準入力)
dic = {
'LLLRB': 'rolling',
'DDRRA': 'upper',
'AAAAA': 'rush'
}
(S を探索する位置を 0 で初期化する)
(S の長さを取得する)
while (Sの長さ未満の時にループする):
(S の現在位置から5文字を取得して変数に代入する)
(もしその変数が辞書のキーに含まれていたら):
(そのキーの値を画面に出力する)
(現在位置から5文字分飛ばす)
(なければ):
(現在位置を1文字分進める)
残り4文字以下を調べても無駄なので、S の長さから -4 してもよいです。