モノグサプログラミングコンテスト2024(AtCoder Beginner Contest 345)の解説記事です。
ABC345 A – Leftrightarrow
問題
問題文の要約は以下の通りです。
問題の要約
与えられた文字列 \(S\) が双方向矢印型の文字列であるか判定せよ。
双方向矢印型の文字列とは、ある正整数 \(k\) が存在し、1個の<
、\(k\) 個の=
、1個の>
をこの順に連結した長さ \(k+2\) の文字列を指します。
解説1
条件を満たすか判定する。
解説
まず入力を文字列で受け取り \(S\) とします。
# 入力
S = input()
条件を満たすのは以下の条件を満たすときです。
- 文字列が
<
で始まっている - 文字列が
>
で終わっている - 文字列の2文字目から最後の一つ前の文字までに
<
や>
が含まれていない
最初に変数をYes
にしておき、それぞれの条件を満たさないときNo
に変更すれば、最終的な結果が得られます。
# 最終的な結果を表す変数
ans = 'Yes'
# 文字列の長さ
n = len(S)
# 文字列が '<' で始まっているか
if S[0] != '<':
ans = 'No'
# 文字列が '>' で終わっているか
if S[n-1] != '>':
ans = 'No'
# 文字列の2文字目から最後の一つ前の文字までに '<' や '>' が含まれていないか
if '<' in S[1:n-1] or '>' in S[1:n-1]:
ans = 'No'
# 出力
print(ans)
解答
# 入力
S = input()
# 最終的な結果を表す変数
ans = 'Yes'
# 文字列の長さ
n = len(S)
# 文字列が '<' で始まっているか
if S[0] != '<':
ans = 'No'
# 文字列が '>' で終わっているか
if S[n-1] != '>':
ans = 'No'
# 文字列の2文字目から最後の一つ前の文字までに '<' や '>' が含まれていないか
if '<' in S[1:n-1] or '>' in S[1:n-1]:
ans = 'No'
# 出力
print(ans)
解説2
条件を満たす文字列と与えられた文字列が等しいか判定する。
解説
条件を満たすのは文字列を最初に構成します。
条件を満たす文字列は1個の<
、n-2
個の=
、1個の>
を連結した文字列なので、これと与えられた文字列を比較します。
# 条件を満たす文字列
ans='<' + '='*(n-2) + '>'
解答
# 入力
S = input()
# 文字列の長さ
n = len(S)
# 条件を満たす文字列
ans='<' + '='*(n-2) + '>'
# 出力
if S==ans:
print('Yes')
else:
print('No')
解説3
正規表現で判定する。
解説
正規表現を使う場合、パターンは ^<=+>$
となります。
^<
は文字列が<
で始まる=+
は=
が1回以上連続する$
は文字列が>
で終わる
つまり、この正規表現は、「文字列が <
で始まり、1つ以上の =
が続き、>
で終わる」というパターンになります。
# 正規表現を用いて判定する
if re.match('^<=+>$', S):
print('Yes')
else:
print('No')
解答
import re
# 入力
S = input()
# 正規表現を用いて判定する
if re.match('^<=+>$', S):
print('Yes')
else:
print('No')
ABC345 B – Integer Division Returns
問題
問題文の要約は以下の通りです。
問題の要約
\(-10^{18}\) 以上、\(10^{18}\) 以下の整数 \(-X\) が与えられるので、\(\lceil \frac{X}{10} \rceil\) を出力せよ。
解説
切り上げ除算の公式を用いる。
解説
a÷bの切り上げ除算は(a+b-1)//b
で計算できます。
今回の場合はX÷10なので(X+9)//10
となります。
まず入力をint
型で受け取り \(X\) とします。
# 入力
X=int(input())
切り上げ除算の結果を出力します。
# 切り上げ除算の結果を出力
print((X+9)//10)
解答
# 入力
X=int(input())
# 切り上げ除算の結果を出力
print((X+9)//10)
ABC345 C – One Time Swap
問題
問題文の要約は以下の通りです。
問題の要約
与えられた文字列 \(S\) の\(i\) 番目の文字と \(j\) 番目の文字を入れ替えます。操作後に得られる異なる文字列がいくつ存在するか求めよ。
解説
\(i\) 番目の文字と \(j\) 番目の文字が同じか異なるかで場合分けを行う。
解説
\(i\) 番目の文字と \(j\) 番目の文字が同じ場合は入れ替えると \(S\) と同じ文字列ができます。
各文字の出現回数に対して、その文字以外の出現回数をかけたものを各文字足し合わせた場合の数の和と\(S\) と同じ文字列ができる場合は+1
したものが求める答えになります。
\(i \lt j\) の条件があるため、最後に回答を2で割っています。
解答
import collections
# 入力
S = input()
# 文字列の長さ
N = len(S)
# 結果を格納する変数
ans = 0
# 各文字の出現回数をカウントする辞書
S_count = collections.defaultdict(int)
for s in S:
S_count[s] += 1
# 同じ文字が2回以上現れるかどうかを判定する変数
S_same = False
# 各文字に対して、その文字を別の文字と入れ替える操作の数を計算
for s in S_count.keys():
# 他の異なる文字と入れ替える場合の数を加算
ans += (N - S_count[s]) * S_count[s]
# 同じ文字が2回以上ある場合、フラグをTrueにする
if S_count[s] >= 2:
S_same = True
# 同じ文字が2個以上あれば、全ての異なる入れ替え操作の後、同じ結果になる場合が少なくとも1つ存在する。
# 同じ文字がなければ、全ての入れ替え操作がユニークであるため、2で割った数が答えになる。
print(ans // 2 + 1 if S_same else ans // 2)
各文字の出現回数はcollections.Counter(S)
で求められるので、次のようにも書くことができます。
import collections
# 入力
S = input()
# 文字列の長さ
N = len(S)
# 結果を格納する変数
ans = 0
# 各文字の出現回数をカウントする辞書
S_count = collections.Counter(S)
# 同じ文字が2回以上現れるかどうかを判定する変数
S_same = False
# 各文字に対して、その文字を別の文字と入れ替える操作の数を計算
for s in S_count.keys():
# 他の異なる文字と入れ替える場合の数を加算
ans += (N - S_count[s]) * S_count[s]
# 同じ文字が2回以上ある場合、フラグをTrueにする
if S_count[s] >= 2:
S_same = True
# 同じ文字が2個以上あれば、全ての異なる入れ替え操作の後、同じ結果になる場合が少なくとも1つ存在する。
# 同じ文字がなければ、全ての入れ替え操作がユニークであるため、2で割った数が答えになる。
print(ans // 2 + 1 if S_same else ans // 2)