ABC324をPythonで解いてみたよ。(A～E問題)

[AtCoder Beginners Contest 324 (ABC324)](https://atcoder.jp/contests/abc324) をPythonで解きました。
見やすいコードを書く練習も兼ねてます。


# A - Same

## 問題ページ
https://atcoder.jp/contests/abc324/tasks/abc324_a

## 考察
セット(集合)は、重複した値を持ちません。
なので、種類数を数えるときにつかえます。
この問題でも、配列 $A$ をセットとして受け取ってしまえば、簡単に判定ができます。

## コード
```Python
N = int(input())
A = set(map(int, input().split()))
if len(A) == 1:
    print("Yes")
else:
    print("No")
```
# B - 3-smooth Numbers

## 問題ページ
https://atcoder.jp/contests/abc324/tasks/abc324_b

## 考察
入力値 $N$ が $2^x3^y$ の形をしているかどうかを判定するのもいいですが、
 $2^x3^y$ の形で表せる数を先にたくさん列挙してしまって、その中に入力値 $N$ があるかどうかを判定すると、実装がラクでいいと思います。

制約は $N \leqq 10^{18}$ です。 $10^{18} < 2^{60}$ なので、 $0\leqq x,y \leqq 60$ の範囲で $2^x3^y$ の値をすべて列挙してセットに格納します。その中に $N$ があるかどうかを判定することで ACできます。

## コード
```Python
se = set()
for i in range(61):
    for j in range(61):
        se.add((2 ** i) * (3 ** j))

N = int(input())
if N in se:
    print("Yes")
else:
    print("No")
```
# C - Error Correction

## 問題ページ
https://atcoder.jp/contests/abc324/tasks/abc324_c

## 考察
文字列 $T$ と $T´$ が同じかどうかは、次の条件で判断すると問題文にありました。

> $T´$ は $T$ から一部が変更されてしまっている可能性があり、具体的には、下記の $4$ つのうちのちょうど $1$ つが成り立つことがわかっています。
>- $T´$ は $T$ と等しい。 
>- $T´$ は、 $T$ のいずれか $1$ つの位置(先頭と末尾も含む)に英小文字を $1$ つ挿入して得られる文字列である。
>-  $T´$  は、 $T$ からある $1$ 文字を削除して得られる文字列である。
>-  $T´$  は、 $T$ のある $1$ 文字を別の英小文字に変更して得られる文字列である。

条件が $4$ つもあって、これを1つ1つ見ていくのはしんどいです。(頑張ればそれでもACできます)

上の $4$ つの条件のうち、 $1$ つ目と $4$ つ目は $T$ と $T´$ の文字数がおなじです。しかも条件もかなり似ていて、 $2$ つの文字列が全く一緒か、 $1$ 箇所だけ異なる文字になっているかです。 $1$ 文字目から $N$ 文字目まで順に見て、異なる文字の数が $1$ つ以下ならオッケー、そうでないならダメと判定すればよさそうです。

条件の$2$ つ目と $3$ つ目も実は似ていて、「長い方の文字列からある $1$ 文字を削除して短い方の文字列になる」かどうかで判定すればいいです。

## コード
```Python
# 2つの文字列の長さがおなじとき
def judge1(A, B):
    diff_cnt = 0
    for a, b in zip(A, B):
        if a != b:
            diff_cnt += 1
    return diff_cnt <= 1


# 2つの文字列の長さが1つちがいのとき
def judge2(A, B):
    # 短い方をA, 長い方をBで固定にする。
    if len(A) > len(B):
        A, B = B, A
    j = 0
    for i in range(len(A)):
        if A[i] == B[j]:
            j += 1
            continue
        j += 1
        if i + 1 != j:
            return False
        if A[i] == B[j]:
            j += 1
        else:
            return False
    return True


N, T = input().split()
N = int(N)
ans_lst = []

for i in range(1, N + 1):
    S = input()
    if len(S) == len(T):
        if judge1(S, T): ans_lst.append(i)
    elif abs(len(S) - len(T)) == 1:
        if judge2(S, T): ans_lst.append(i)

print(len(ans_lst))
print(*ans_lst)
```
# D - Square Permutation

## 問題ページ
https://atcoder.jp/contests/abc324/tasks/abc324_d

## 考察
$0^2, 1^2, 2^2, \cdots$ と平方数を順番に見ていき、その平方数を並び替えて入力値の $S$ をつくれるかどうかを1つ1つ見ていきます。
桁数が足りない分は、先頭に $0$ をつけて桁数を揃えます。 <code>s.zfill(N)</code> で文字列 $s$ を $N$ 桁にするように先頭に $0$ をつけた文字列をゲットできます。
全体の個数が等しいかどうかはcollections.Counterをつかうと実装がラクです。


## コード
```Python
from collections import Counter

N = int(input())
S = input()

max_num = 10 ** N
C = Counter(S)
sq = 0
ans = 0

while (num := sq * sq) < max_num:
    str_num = str(num).zfill(N)
    if Counter(str_num) == C:
        ans += 1
    sq += 1

print(ans)
```
# E - Joint Two Strings

## 問題ページ
https://atcoder.jp/contests/abc324/tasks/abc324_e

## 考察
たとえば、 $S_1=$"abba", $S_2=$"bcb" $T=$"bac" のとき、$S_1+S_2=$"abbabcb" となり、(連続してなくてもいい)部分文字列として "bac" があってokです。こうなるような$S_1, S_2$ のペアを求める問題です。

文字列 $S_i$ について、文字列 $T$ の先頭何文字が部分文字列になるか・文字列 $T$ の末尾何文字が部分文字列になるか、の$2$ つが分かればいいわけです。
先頭何文字が部分文字列になるかは、文字列 $S_i$ の先頭から貪欲に調べます。
末尾何文字が部分文字列になるかは、文字列 $S_i$ と文字列 $T$ をどちらも反転させて、「($S_i$を反転させた文字列)について、($T$を反転させた文字列) の先頭何文字が部分文字列になるか」を考えればいいです。
そして、(先頭 $x$ 文字が $T$ の部分文字列になる文字列 $S_1$ ) と (末尾 $y$ 文字が $T$ の部分文字列になる文字列 $S_2$) をくっつけたとき、 $x+y\geqq len(T)$ だったらokです。

各 $x$ について、 $len(T)-x$ 以上の $y$ がいくつあるかを調べればいいです。これは2分探索で求められます。 



## コード
```Python
from bisect import bisect_left

# 文字列Bの先頭何文字が文字列Aの部分文字列になっているか
def f(A, B):
    j = 0
    for i, a in enumerate(A):
        if a == B[j]:
            j += 1
            if j == len(B):
                break
    return j


N, T = input().split()
N = int(N)
S = [input() for _ in range(N)]

rev_T = T[::-1]
left = [0] * N
right = [0] * N
for i in range(N):
    left[i] = f(S[i], T)
    right[i] = f(S[i][::-1], rev_T)

right.sort()
ans = 0
for le in left:
    ans += N - bisect_left(right, len(T) - le)
print(ans)
```