くろたんく雑記帳

日常とか、わんちゃんとか、機械学習とか、競プロとか、

MENU

Python3で解く AtCoder Regular Contest 110 B - Many 110

きれいに条件分岐ができればいい。

最初'0'にアサインできたら、最後、左から数えて何番目の'0'にアサインできるかを考える問題

概要

問題

  • 文字列{\displaystyle s}は'110'が{\displaystyle 10^10}個連結したものである。
  • {\displaystyle n}文字の文字列{\displaystyle t}が与えられる。
  • {\displaystyle s}{\displaystyle t}が連続する部分文字列としていくつ含まれる?

解くときに考えた内容

  • {\displaystyle t}の長さ別に考える。
  • {\displaystyle t}が1文字の場合
    • {\displaystyle t} = '1'の場合
      • {\displaystyle 2 \times 10^{10}} ('110'に2つ'1'があるから)
    • {\displaystyle t} = '0'の場合
      • {\displaystyle 10^{10}} ('110'に1つ'1'があるから)
  • {\displaystyle t}が2文字の場合
    • {\displaystyle t} = '11'と'10'の場合
      • {\displaystyle 10^{10}} ('110'に1つ'11'または'11'があるから)
    • {\displaystyle t} = '01'の場合
      • {\displaystyle 10^{10}-1} (最後の1つは取れないので-1)
    • {\displaystyle t} = '00'の場合
      • {\displaystyle 0}(このパターンはないので)
  • {\displaystyle t}が3文字以上の場合は0が {\displaystyle 10^{10}}があるので、{\displaystyle t}の0のどこまでアサインできるかということになる。
    • {\displaystyle t}の長さを3で割って切り上げる。
    • この値は{\displaystyle t}に含まれる0の個数であり、cntとする。
    • ここで、'110'をcnt分連結した文字列に含まれるかどうかで、カウント対象であるかと、その文字列が0で終わっているのか1で終わっているのかを同時に判定する(結構キモ)
    • '110'をcnt分連結した文字列にtが含まれるなら、tが0終わりでsに存在ということ。
      • 例えば、
        • 0110とすると、cnt = 2であり、{\displaystyle s}の0の最後……11'0110'、{\displaystyle 10^{10} - 1}個目まで取れる。
        • 0110110とすると、cnt = 3であり、{\displaystyle s}の0の最後……11'0110110'、{\displaystyle 10^{10} - 2}個目まで取れる。
      • 一般に、{\displaystyle 10^{10} - (cnt - 1)}となる。
    • '110'をcnt分連結した文字列にtが含まれず、'110'をcnt分+1連結した文字列にtが含まれるなら、tが1終わりでsに存在ということ。
      • 例えば、
        • 011とすると、cnt = 1であり、{\displaystyle s}の0の最後……11'011'0、{\displaystyle 10^{10} - 1}個目まで取れる。
        • 011011とすると、cnt = 2であり、{\displaystyle s}の0の最後……11'011011'0、{\displaystyle 10^{10} - 2}個目まで取れる。
      • 一般に、{\displaystyle 10^{10} - cnt}となる。
      • これらに当てはまらないものは {\displaystyle s}{\displaystyle t}が含まれないので {\displaystyle 0}

f:id:black_tank_top:20201206095202j:plain
部分文字列のカウントイメージ

上図のように、 {\displaystyle t}が3文字以上で{\displaystyle s}に含まれるその他のパターンも同じ様に書いてみると、最初'0'にアサインできたあと、最後の左から数えて何番目の'0'にアサインできるかを考える問題であると言うことがわかる。

コードは全場合分けベースで書いたものと、 {\displaystyle t}が2文字のものも3文字のものと同様に考えて問題ないので、2種類にかき分けた。


コード

全部場合分け
n = int(input())
t = input()
m = 10**10
# 切り上げ
if len(t) % 3 == 0:
    cnt = len(t) // 3
else:
    cnt = len(t) // 3 + 1
# sにtが存在する出力パターン
# tのサイズが1のとき
if t == '1':
    print(2*m)
elif t == '0':
    print(m)
# tのサイズが2のとき
elif t == '11' or t == '10':
    print(m)
elif t == '01':
    print(m-1)
elif t == '00':
    print(0)       
# tのサイズが3以上のとき
elif len(t) >= 3:
    # この書き方ならt = 0, 11などもここに含めても可能
    # 個数倍復元して含まれているなら、tが0終わりでsに存在
    if t in '110' * cnt:
        print(m-cnt+1)
        exit()
    # 上記でなくて個数倍+1で復元して含まれているなら、tが1終わりでsに存在
    elif t in '110' * (cnt+1):
        print(m-cnt)
        exit()
    # 上記以外はsにtが存在しない出力パターン  
    else:
        print(0)
最低限
n = int(input())
t = input()
m = 10**10
if len(t) % 3 == 0:
    cnt = len(t) // 3
else:
    cnt = len(t) // 3 + 1
# tが1のとき
if t == '1':
    print(2*m)
    exit()
# それ以外の時(tが0や、2文字以上のときはこの記述で集約できる)
if t in '110' * cnt:
    print(m-cnt+1)
    # 上記でなくて個数倍+1で復元して含まれているなら、tが1終わりでsに存在
elif t in '110' * (cnt+1):
    print(m-cnt)
# 上記以外はsにtが存在しない出力パターン  
else:
    print(0)

参考になる書籍

けんちょんさんこと、大槻さんの書籍である、「問題解決力を鍛える!アルゴリズムとデータ構造」 ご本人の記事の中で難易度は、螺旋本ということで初学者でも非常に結構読みやすい!!!(実際に、けんちょんさんの記事はC++に疎い自分でも、その思考プロセスを学べるくらい丁寧に書かれているし、この書籍も非常に読みやすい)どのようなステータスの人にもおすすめの一冊になっている。

とうとう発売され自分の手元にも来た(2020-10-03)。現在読み中なのである程度読み込んだら感想を書く予定。めっちゃ図が多いし、章末問題もあるしかなりイケてる。答えはC++で書かれているがそのうちPythonでも書かれるとのことなので、自分で前もってやってしまおうと思う(答え合わせできるのはありがたい)。

7章までは別記事に残してある。 blacktanktop.hatenablog.com