競プロ学びながらの気づきメモ

ミスのグループ化(提出前チェック項目作り)

- インデント忘れ(loop内でやるべき処理なのに、外に出てたり。)

- 関数やループの後の":"忘れ

- 関数の def 忘れ

 

 

 

 

 

2024/3/31

 

# 二重ループにならないで済む実装がないか考える

円周率の小数点以下に、0~9が各何回出るか数える

0~9に関してループして、それぞれ頭からお尻まで見ると二重ループ

頭からお尻まで見ながら、今見てる数のグループのcntを+1すれば一回の確認ですむ。

問題のリンクは忘れた。

 

# 3個前のアルファベットにするシーザー暗号

単純にord()とchr()使うと、aやbやcが溢れる。aはzに変換。という例外を持たせた関数を3回走らせるようにすれば、例外の記述はaの時だけですむ。(自分で実装した時はordが96以下の時+26する、という対応表見ながらじゃないとわからないマジックナンバー入りの実装になってた。)

 

algo-method.com

 

chr() とord()

ord()の方が文字コードの数字を返してくれる。chr()はその逆

 

 

listのアンパック

mylist=[1,2,3]

print(*mylist)

→1 2 3

printは複数個をカンマ区切りで渡せて、その場合空白スペース区切りで表示する

 

 

print()したときに下記のような変のものが出る時

<__main__.Tortoise object at 0x...>

これは、自作したTortoiseクラスのインスタンスをprintしようとしている

Tortoiseクラスのインスタンスは、print()されたときにどういう挙動すべきか定義されてないからこうなる。

逆にいうと、他のstringとかlistとかもクラスであり、それらはprint()された時の挙動が最初から定義されているから、直感的なprint()による表示ができる。

定義するには__str__ メソッドを使う

    def __str__(self):
        return f"name: {self.name}"

https://algo-method.com/tasks/1278

 

 

# defaultdictが便利。辞書作るときに初期値に何か設定しておけるので、後からいじりやすい。

from collections import defaultdict
# 各有権者が投票した立候補者の名前
votes = ["aruru", "iruru", "ururu", "aruru", "eruru", "oruru", "aruru", "iruru", "aruru", "eruru", "iruru", "ururu", "eruru"]

d = defaultdict(int) # 空の辞書型変数
for vote in votes: # 投票された立候補者に 1 を足す
d[vote] += 1

# 投票結果を出力
for name, value in d.items():
print(f"{name} {value}")

https://algo-method.com/tasks/1294

 

 

 

#順列や組み合わせを出す時はiteretoolsを使う

nPr nCr  

iteretools.permutations(iterable, r)

itertools --- 効率的なループ実行のためのイテレータ生成関数 — Python 3.12.2 ドキュメント

https://algo-method.com/tasks/1315/editorial

 

# str.join(iterable)で iterableの各要素をstrを間に入れてくっつけられる

 

 

# 空白文字で複数入力があるときはsplitだけど、改行されて2個入力されるときは普通に2つinput()作る

S = input()
T = input()

if S == T:
print("Yes")
else:
print("No")
 
#こうではない
S, T = map(int, input().split())
 

https://algo-method.com/tasks/31

 

 

# popは要素へのアクセスと削除を同時に行う

 

 

# 可変長配列であるlistから特定の順番の要素を削除したり、追加したりするのは時間がかかる

- 先頭要素を消したら、それより後ろの要素全てを前に詰めてindex振り直すみたいな作業が必要

- 先頭要素を追加しても一緒。

- 逆に末尾に削除したり追加したりする分には、既存のものに影響ないからO(1)

- なので先頭に足したりしたい時は、反転してから足すと良い

- ちなみに反転はO(n)。後ろの要素から一個ずつ新しいリストに足したく感じかな?

https://algo-method.com/tasks/832

 

 

2024/4/13

配列の要素削除/挿入がO(N)なのは何故か

・配列はメモリの中で連続したアドレスにデータを配置

 →ランダムアクセスがO(1)

  →mylist[i]のアドレスを下記の式でパッと1回で計算できるので、そのメモリにたどり着ける

mylist[i]のアドレス = 先頭のアドレス + 1データのサイズ*i 

 

逆に、連続したアドレスにデータ入れとかないといけないので、途中で一個抜いたりすると、それより後のやつを全部前にずらさないといけなくてO(N)

→liked listなら、O(1)で済む

 

tech.connehito.com

 

 

 

* 演算子で、ミュータブルな型を並べたリストを作成する場合、参照渡しによるコピーに気をつける必要がある。

ミュータブルな型(リストとか)に対して、*を使うと、参照渡しによるコピーになる

# 「0 を 3 回繰り返したリスト」を 2 回繰り返したリスト
2# 各要素の内容は実は同じもの
3a = [[0] * 3] * 2
4print(a) # 出力: "[[0, 0, 0], [0, 0, 0]]"
5
6a[0][0] = 1 # 0 番目のリストの 0 番目の要素を 1 に変更する
7print(a) # 出力: "[[1, 0, 0], [1, 0, 0]]"

リスト内包表記によるコピーで防ごう。

# 「0 を 3 回繰り返したリスト」を 2 回生成して追加したリスト
2# 各要素の内容は独立している
3a = [[0 for j in range(3)] for i in range(2)]
4print(a) # 出力: "[[0, 0, 0], [0, 0, 0]]"
5
6a[0][0] = 1 # 0 番目のリストの 0 番目の要素を 1 に変更する
7print(a) # 出力: "[[1, 0, 0], [0, 0, 0]]"

 

algo-method.com

 

# リストへの*演算子の適用

・リストの中身を繰り返した新たなリストを作成する