aacord’s memo

abcを中心にpythonで解いた問題のメモ、整理をしています。緑になった。

abc 124 d 'handstand' (python)

直観的に一回の動作で、1と1との間にある0を全て1にする、
次の動作では、前1にしたところより1つ右にある0の塊を全て1にする…
という動作をk回繰り返したものの中から連続して1が並ぶ最大値が作れるとわかる
Sのなかに0が連続していくつあるか、1が連続していくつあるかのリストを作り、
2つのリストを足すことで一回の動作で連続する1の個数が作れる
あとはこのリスト a を a[ i ] から a[ i+k-1 ] まで足せばいいのだが、毎回 sum をとっていると TLE するので、はじめにリストを累積和 (a[ i ] を a[ 0 ] ~ a[ i-1 ] の総和)にしておいて、k離れた a[ i ] を引いて求めた
累積和のリストが欲しいときは numpy をつかって np.cumsum(ab,out=ab) とするのが便利
最初が 0 で始まると決めてリストを作った(最初が1から始まる時はその前に0が'0個'並んでいるとしてまず0のリストに'0'を追加した)

def main():
  import sys
  input = sys.stdin.readline
  import numpy as np
  n,k = map(int,input().split())
  s = input()
  chk_0 = []
  chk_1 = []
  c0 = 0
  c1 = 0
  if s[0]=='0':
    c0 += 1
  else:
    c1 += 1
    chk_0.append(0)

  for i in range(n-1):
    if s[i]=='0' and s[i+1]=='0':
      c0 += 1
    if s[i]=='0' and s[i+1]=='1':
      chk_0.append(c0)
      c0 = 0
      c1 = 1
    if s[i]=='1' and s[i+1]=='0':
      chk_1.append(c1)
      c0 = 1
      c1 = 0
    if s[i]=='1' and s[i+1]=='1':
      c1 += 1

  if c0 != 0:
    chk_0.append(c0)
    chk_1.append(0)
  elif c1 != 0:
    chk_1.append(c1)
    chk_0.append(0)

  if len(chk_0) <= k:
    print(n)
    exit()

  ab = np.array([x+y for x,y in zip(chk_0,chk_1)])
  np.cumsum(ab,out=ab)

  ans = ab[k-1]  # 最初はSの左端からk個の0の塊を1にしたときの値にしている
  for i in range(len(ab)-k):
    chk = ab[k+i]-ab[i]
    chk += chk_1[i]  # 両端が1になったほうが大きいので、chk の左端に1つ左隣りの1の塊を足す
    ans = max(ans,chk)
  print(ans)
if __name__ == '__main__':
    main()