🔙
🔝

Bランク実力確認問題セット1

ヒントを頼りにプログラムを組んでみてください。

外貨計算

外貨計算

問題文を理解しましょう

問題文を理解しましょう

    私が子供の頃は、まだ1ドル=360円の時代でした。なので、こんななぞなぞがありました。

    問『ハンドルは何円?』

    答『180円』(半ドルだから)

    現在は相場が変動するのでリアルタイムに調べないと答えられませんが😂、当時は固定だったのです。

    問題文に『ドルを示す正整数 N が与えられるので、N に 360 を掛けた値を出力してください。』とあります。1ドル=360円と定義されていますので、Nドル = N×360円です。

タイピング練習

タイピング練習

方法はいつもひとつとは限らない

方法はいつもひとつなんてありえない

    真っ先に 文を思い浮かべるのは自然なことです。しかし方法は1つしかないなんてことはありえません。


    Python3 では、こんな機能があったことを覚えていますか?

    こんな機能

    表を作成するときに、ラインを引いたりする際に使ったことがあるかもしれません。あとはこれを使ってどうにかすれば、 文を使わない方法で結果を実現させることができるはずです。

taぬき

taぬき

考え方

考え方

    文字列に含まれる 't' と 'a' を削除したいわけですが、大きく分けて、

    1. リストに変換してから 't' と 'a' をアレしてから、最後に文字列に戻す
    2. 文字列のまま 't' と 'a' をアレする

    それでは早速アレしてみましょう。😊

リストに変換してから 't' と 'a' を取り除く その1

要素を指定して取り除く

    「取り除く」といったら アレ でしょう。ただし取り除けるのは1回につき1つのみです。さらに該当文字が無ければエラーが返されてしまいます。

    't' と 'a' の文字数を予めカウントしておき、それぞれの回数分アレして取り除くとかしちめんどくさいなもう!


    やってみてください。😊

リストに変換してから 't' と 'a' を取り除く その2

要素番号を指定して取り除く

    もう1つ、取り除くといったら コレ もアリでしょう。

    1. 't' もしくは 'a' の要素番号を調べ、あったらコレして取り除く
    2. どちらもなければ除去完了

    要素番号を調べる際、 .index() メソッドをそのまま使ってしまうと、やがてエラーとなります。どうしましょ?


    めんどくさいからやっぱりナシだけど、やってみてください。

リストに変換してから 't' と 'a' を取り除く その3

手動で線形探索して取り除く

    単純に先頭である要素0 から順に 't' と 'a' を探して該当要素を取り除く方法です。この方法だとエラーが起こりにくいです。

    要素番号を使って取り除く方法全てで使えますね。一回の探索で済みますが、効率良いとはちょっと言い難いかな?1つ要素を取り除くと、それ以降の要素が全て前に詰められる処理が発生するからです。

    要素番号012345678
    Smetditaum

    ↓ 要素番号 2 の 't' を取り除く

    要素番号012345678
    Smeditaum

    ↓ 'd' を前に詰める

    要素番号012345678
    Smeditaum

    ↓ 'i' を前に詰める

    要素番号012345678
    Smeditaum

    ↓ (以下略)

    要素番号01234567
    Smeditaum

    前に詰める処理が6回発生しました。この処理は、文字列が長いほど、そして 't' や 'a' が多いほど時間がかかってしまいます。

リストに変換してから 't' と 'a' を取り除く その4

ヌルストリングに置き換える

    除去する度に前に詰めていく処理の無駄を軽減するために、't' と 'a' の所を一時、'' (ヌルストリング) に置き換えておくのはどうでしょう?

    ['m', 'e', 't', 'd', 't', 'i', 'a', 'u', 'm']

    ['m', 'e', '', 'd', '', 'i', '', 'u', 'm']

    このリストを ''.join() で文字列に変換すると、

    medium

    となります。いいね。👍

文字列から 't' と 'a' を取り除く その1

文字列には文字列メソッドを

    文字列には、文字を取り除くための関数やメソッドがありません。考え方を変えて、あるものをないものに変えてしまいましょう。

    これとかどうよ

    このメソッドは指定した文字を一気に変換してくれるのですが、't' と 'a' のように、複数の文字列を一度に指定することが出来ません。ですので、't' の分と 'a' の分、メソッドを計2回実行する必要があります。

文字列から 't' と 'a' を取り除く その2

そこで・・・

    't' と 'a' の2回分だけならまだしも、これが10文字、100文字と増殖していく度に回数分のメソッドを書いていくこととなってしまいます。文の出番です。

    あるものをないものに変えてしまうという方法は変えずに、この機能を必要回数分繰り返せるようにします。単純に「あるものリスト」を作成して1つずつメソッドの「あるもの」に当てはめていくだけです。こうすれば文字列の変更や増減が自在になります。

文字列から 't' と 'a' を取り除く その3

新しく文字列をつくる


    【 Copilot が勝手に記述 】

    リストに変換してから 't' と 'a' を取り除く方法もありますが、もう一つの方法は新しく文字列を作ることです。

    具体的には、't' と 'a' 以外の文字を集めて新しい文字列を作るという方法です。


    先に書かれた!🤣

    線形探索で文字列の先頭から1文字ずつチェックしていき、't' または 'a' 以外の時、空の文字列で初期化した変数に代入していきます。't' と 'a' の時は無視します。

    これを文字列の最後まで続ければ、最終的に taぬき の文字列が出来上がるというすんぽーなのだ。

格闘ゲーム

格闘ゲーム

問題を理解する

問題を理解する

    格闘ゲームを知らない人には難題かと思われます。そんな方のために少し解説をしますが、昇龍拳やアキラ3連コンボの猛特訓をしていた過去がある方には不要と思われますので読み飛ばしちゃってください。

    ゲームコントローラの十字ボタン(またはレバー)と、AボタンとBボタンがあるようです。ゲームコントローラすらわからない方は、

    ゲームコントローラ - Wikipedia

    に現物画像がありますので、そちらで確認してください。

    'L', 'R', 'U', 'D' は、それぞれ十字ボタンの「←」「→」「↑」「↓」でしょう。Aボタンが「パンチ」、Bボタンが「キック」と思われます。

    rollingupperrush という3種類の技を作ります。

    技名コマンド操作
    rollingLLLRB←←←→B
    upperDDRRA↓↓→→A
    rushAAAAAAを5回

    それぞれの技を使う為には、この通りにボタンを入力する必要があります。ピンと来ない方は、タイピングゲームのように、例えば 'DDRRA' とキーを打つと「upper」が使えると考えても差し支えありません。

    条件を確認してみましょう。

    • 1 ≦ |S| ≦ 100000
    • |S| は文字列 S の長さ
    • S は 'R', 'L', 'D', 'U', 'A', 'B' のいずれかからなる文字列
    • S にはコマンドの文字列が 1 つ以上含まれることが保証される

    文字列10万文字くらいなら強引なやり方をしてもイケちゃいそうです。出力する技名は、最低でも1つはあるということになります。何も技が出せなくて出力が空になるということはありません。

    プログラムを組む上で最も考慮すべきところが、

    『ただし、1 つのボタン入力は 1 つのコマンドにしか使用されません。例えば、"DDRRAAAAA" という入力があった場合、"DDRRA" はコマンド「upper」として認識しますが、残る入力は "AAAA" となるためコマンド「rush」は認識されないことに注意してください。』

    この部分は次に説明します。

コマンド技の発動条件

コマンド技の発動条件

    【入力 S】

    DDRRAAAAALLLRB

    問題集の入力例を使って説明します。

    1
        DDRRAAAAALLLRB
    

    各技のコマンド入力数がすべて5ですので、5文字ずつ調べていきます。

    DDRRAAAAALLLRB

    まずここで何らかの技とコマンドが一致するかを調べます。

    技名コマンド操作
    rollingLLLRB←←←→B
    upperDDRRA↓↓→→A
    rushAAAAAAを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()関数で毎回チェックする? え、するの?


  • ここまで書いておいてなんですが、私ならこの方法は選びません。

置換とかどうかな?

置換、ダメ? 絶対?

    リアルタイムでコマンド入力して処理する場合は、不要となった入力は破棄してしまえばよいのですが、全ての入力から順に技をピックアップしていくとなると考え方を少し変えなければならないようです、たぶん。

    もう、ゲームとかリアルタイムとか、そういうの全部無視して、この問題をラクにクリアするためだけの方法でやっつけちまいましょう。

    技名コマンド操作
    rollingLLLRB←←←→B
    upperDDRRA↓↓→→A
    rushAAAAAAを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回行うことになりますので、効率いいとは言えないですね。😓

1回の線形探索でしっかりと

1回の線形探索でしっかりと

    今度は先頭から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 してもよいです。