自分用メモ

プログラミングとかのメモを書きたいです

競プロ典型90問:003 - Longest Circular Road(★4)

問題

https://atcoder.jp/contests/typical90/tasks/typical90_c

挑戦結果

  • 解けなかった

考えたこと

  • ワーシャルフロイドで全ノード間の距離を求めて、その最長の距離に+1すれば良いと思った。
  • コード書いたけどTLEした。$N3$では無理だった。
  • グラフの直径を求めれば良いことがわかった。ググったところアルゴリズムは理解した。
  • ただ、上手に実装できなくてTLEした。

木の直径を求めるアルゴリズム

  1. 任意の頂点から最長の頂点uを探索
  2. 頂点uから最長の頂点vを探索
  3. uとvを結ぶパスが最長

解説を読んだふりかえり

  • グラフをマトリックスで持ったことで計算量が上がっているようだった。

ソース

from collections import deque
N = int(input())
INF = float('inf')
# グラフ
G = [[] * (N+1) for _ in range(N+1)]
for _ in range(N-1):
    a, b = [int(x) for x in input().split()]
    G[a].append(b)
    G[b].append(a)
# stからの距離を配列で返す
def getdist(st):
    visited = {st}
    dist = [INF] * (N+1)
    dist[st] = 0
    Q = deque([st])
    while len(Q)>0:
        node = Q.popleft()
        for nextnode in G[node]:
            if nextnode in visited:
                continue
            dist[nextnode] = min(dist[nextnode], dist[node]+1)
            visited.add(nextnode)
            Q.append(nextnode)
    return dist

## グラフの直径を求める 
# 任意の場所stから各ノードへの最短距離を求める
st1 = 1
dist1 = getdist(st1)
# stからの距離が最大なノード(idx)を求める
d = idx = 0
for i in range(2,N+1):
    if d<dist1[i]:
        d=dist1[i]
        idx = i
# idxから各ノードへの最短距離を求める
dist2 = getdist(idx)
# 各最短距離の中で、最大の値がグラフの直径。 今回の問題は直径+1が答え。
print(max(dist2[1:]) + 1)

競プロ典型90問:002 - Encyclopedia of Parentheses(★3)

問題

https://atcoder.jp/contests/typical90/tasks/typical90_b

挑戦結果

  • 解けた
  • 時間:20分ぐらい

考えたこと

  • すべて列挙なのでループして求める
  • N文字なら、開カッコがN/2個、閉カッコがN/2個
  • 閉じカッコが先行する場合がNG
  • (と)の全順番を$2N$パターン作成してチェックするのもアリかと思ったけど、再帰的にOKパターンだけ作れそうな気がした
  • 方針
  • 左から確定していき全パターンを保持。(と)の数を持ちつつ、末尾に追加していく。

振り返り

  • とりあえず解けたのは良かった。
  • 解説は「ステップ1:bit全探索」+「ステップ2:'(' - ')(の累積計算」だった。自分はステップ2を思いついていなかった。
  • 解説を読んだあとにbit全探索版でもやってみたら、TLEになった。
  • pypy3 なら通った

コード

# インプット
N = int(input())

# n:残りの長さ
# op:( の数
# cl:) の数
# L:現時点でリストアップしているカッコ文字列のリスト
# 戻り値:カッコ文字列のリスト
def f(n, op, cl, L):
    # 残りが0文字なら、Lを返す
    if n==0:
        return L
    ret = []
    # カッコが開いてるときは、閉じカッコを末尾に追加可能
    if op > cl:
        L1 = [s+')' for s in L]
        ret += f(n-1, op, cl+1, L1)
    # 開きカッコを使い切ってなければ、開くカッコを末尾に追加可能
    if op < N//2:
        L2 = [s+'(' for s in L]
        ret += f(n-1, op+1,cl, L2)
    return ret

# Nが奇数のときはNG
ans = []
if N%2==0:
    # 初期値は長さNの文字列が欲しい、開きカッコも閉じカッコも0、既存リストは空文字。
    ans = f(N, 0, 0, [""])
# ソート
ans.sort()

# 結果の出力
for a in ans:
    print(a)

ソース(解説読んだあとにBit全探索版で書き直し)

# インプット
N = int(input())

# i番目のビットが立っているか
BIT_LENGTH = N
def isOne(bit_pat, i):
    return (bit_pat & 1 << (BIT_LENGTH - 1 - i)) > 0

# 結果用
ret = []

# bit全探索で全パターン列挙
for bit_ptn in range(1<<N):
    # 開きカッコを+1、閉じカッコを-1でカウント
    kakko_cnt = 0
    # カウントがマイナスになったらNGになる
    isOK = True
    # カッコの開きと閉じをカウント
    for i in range(N):
        if isOne(bit_ptn, i):
            kakko_cnt += 1
        else:
            kakko_cnt -= 1
        # 一時的にでもマイナスになったらNG 
        if kakko_cnt < 0:
            isOK = False
    # 開きカッコと閉じカッコの数が同数ならOK
    if isOK and kakko_cnt==0:
        # bitから文字列に変換
        s = ""
        for i in range(N):
            if isOne(bit_ptn, i):
                s += "("
            else:
                s += ")"
        ret.append(s)
# ソートして出力
ret.sort()
for s in ret:
    print(s)

競プロ典型90問:001 - Yokan Party(★4)

問題

https://atcoder.jp/contests/typical90/tasks/typical90_a

挑戦結果

  • 解けなかった
  • 時間:30分ぐらい

考えたこと

  • 苦手なDPと思ってずっと考えていたけど、あきらめた。
  • 解説を見たら「答えで二分探索」の問題だった

解説を読んだあとのふりかえり

  • 長さM以上のピースに分割できるかの関数を作った。けっこうハマって時間がかかった。
  • 作成した関数をforで回して結果を確認。この戻り値がTrue→Falseになる瞬間を二分探索でもとめればOKであることを確認。
  • 二分探索を作成。これはスムーズにできた。

ソース

# Keyword:答えで二分探索

# インプット
N, L = [int(x) for x in input().split()]
K = int(input())
A = [int(x) for x in input().split()]
# 計算用に先頭と末尾に加えておく
A = [0] + A + [L]

# 長さM以上のピースに分割できるか?
def check(M):
    l = cnt = 0
    for i in range(N+1):
        l += (A[i+1] - A[i])
        if l>=M:
            l=0
            cnt +=1
    # return cnt > K or (cnt == K and l>=M)
    return cnt > K 

#### デバッグ・確認用
# 二分探索をせずに各スコアのの実現可否の列挙
# Trueである最も大きい値がこの問題の答え。
# for i in range(1,L):
#     print(f"{i}, {check(i)}")

# 二分探索。lはTrue、rはFalseを想定。
def binarysearch(l,r):
    # 終了条件
    if l+1 == r:
        return l
    # lとrの中間のTrue/Falseで範囲を狭くする
    m = (l + r) //2
    if check(m):
        return binarysearch(m, r)
    else:
        return binarysearch(l, m)

# 二分探索で求める
print(binarysearch(1, L))

ラズパイに差したHDDをsambaでWindowsに公開

exfatでフォーマットしたUSBのHDDをマウントすることができたので、そのフォルダをsambaで公開できるようにした。

qiita.com

f:id:ebinafactory:20210523175406p:plain
apt-getでsambaをインストール

いいえ

インストール完了

f:id:ebinafactory:20210523201923p:plain
smb.confの末尾に追記。認証などはなし。

再起動

ラズパイにUSBのHDDをつなげる

背景

USBのHDDを購入した。NTTのPR-500MIのUSBポートにつないで簡易NASにしようと思ったのに、つないでもどうも使えない。 PR-500MIにはUSBポートもあるし、Web画面にはUSBストレージ機器についてのメニューもあるのに、簡易NAS的な使い方はできないらしい。。。

騙された設定画面

f:id:ebinafactory:20210523174656p:plain
PR-500MIの騙された画面

USBのHDDの自動マウント

このサイトを参考にさせてもらった。 kowaza.blue

f:id:ebinafactory:20210523172944p:plain
アンマウント

f:id:ebinafactory:20210523173021p:plain
フォルダ作成とパーミッション変更

f:id:ebinafactory:20210523173101p:plain
バイスの確認

f:id:ebinafactory:20210523201549p:plain
exfat用(必要だったかは不明)

f:id:ebinafactory:20210523201708p:plain
fstabの設定

参考サイトと一点違うかなというのは、自分はMacも持っているので、NTFSよりexfatのほうが便利かと思ってHDDをexfatでフォーマットしている。設定内容もそれにあわせてexfatにした。

SakuraVPSのCentOS7でDockerのインストール

とりあえず、dockerの依存性を見てみる。

$ yum deplist docker
読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: www.ftp.ne.jp
 * epel: mirror.dmmlabs.jp
 * extras: www.ftp.ne.jp
 * updates: www.ftp.ne.jp
パッケージ    : docker.x86_64 2:1.13.1-53.git774336d.el7.centos
  依存性      : /bin/sh
   provider: bash.x86_64 4.2.46-29.el7_4
  依存性      : docker-client = 2:1.13.1-53.git774336d.el7.centos
   provider: docker-client.x86_64 2:1.13.1-53.git774336d.el7.centos
  依存性      : docker-common = 2:1.13.1-53.git774336d.el7.centos
   provider: docker-common.x86_64 2:1.13.1-53.git774336d.el7.centos
  依存性      : libassuan.so.0()(64bit)
   provider: libassuan.x86_64 2.1.0-3.el7
  依存性      : libaudit.so.1()(64bit)
   provider: audit-libs.x86_64 2.7.6-3.el7
  依存性      : libc.so.6(GLIBC_2.17)(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  依存性      : libdevmapper.so.1.02()(64bit)
   provider: device-mapper-libs.x86_64 7:1.02.140-8.el7
  依存性      : libdevmapper.so.1.02(Base)(64bit)
   provider: device-mapper-libs.x86_64 7:1.02.140-8.el7
  依存性      : libdevmapper.so.1.02(DM_1_02_97)(64bit)
   provider: device-mapper-libs.x86_64 7:1.02.140-8.el7
  依存性      : libdl.so.2()(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  依存性      : libgpg-error.so.0()(64bit)
   provider: libgpg-error.x86_64 1.12-3.el7
  依存性      : libgpgme.so.11()(64bit)
   provider: gpgme.x86_64 1.3.2-5.el7
  依存性      : libgpgme.so.11(GPGME_1.0)(64bit)
   provider: gpgme.x86_64 1.3.2-5.el7
  依存性      : libgpgme.so.11(GPGME_1.1)(64bit)
   provider: gpgme.x86_64 1.3.2-5.el7
  依存性      : libpthread.so.0()(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  依存性      : libpthread.so.0(GLIBC_2.2.5)(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  依存性      : libpthread.so.0(GLIBC_2.3.2)(64bit)
   provider: glibc.x86_64 2.17-196.el7_4.2
  依存性      : libseccomp.so.2()(64bit)
   provider: libseccomp.x86_64 2.3.1-3.el7
  依存性      : libsystemd.so.0()(64bit)
   provider: systemd-libs.x86_64 219-42.el7_4.10
  依存性      : libsystemd.so.0(LIBSYSTEMD_209)(64bit)
   provider: systemd-libs.x86_64 219-42.el7_4.10
  依存性      : rtld(GNU_HASH)
   provider: glibc.x86_64 2.17-196.el7_4.2
   provider: glibc.i686 2.17-196.el7_4.2
  依存性      : systemd
   provider: systemd.x86_64 219-42.el7_4.10

特に気にせずインストール

Dockerの公式サイトに手順が載っているけど、無視してコマンド一発で済ませる。 Get Docker CE for CentOS | Docker Documentation docker-ceとdocker-eeの内容の違いは不明。(コミュニティーエディションとエンタープライズエディションらしい)

sudo yum install docker
docker --version
→ Docker version 1.13.1, build 87f2fab/1.13.1

バージョン1.13は結構古いみたいだけど、気にしないことにする。

起動と自動起動

sudo systemctl start docker
sudo systemctl enable docker
→ Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

Dockerの起動(ただしrootで)

docker run ubuntu:14.04 /bin/echo 'Hello world'
→ /usr/bin/docker-current: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.26/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See '/usr/bin/docker-current run --help'.

権限のエラーが出てしまった。とりあえずrootで再実行。

sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
→
Unable to find image 'ubuntu:14.04' locally
Trying to pull repository docker.io/library/ubuntu ... 
14.04: Pulling from docker.io/library/ubuntu
324d088ce065: Pull complete 
2ab951b6c615: Pull complete 
9b01635313e2: Pull complete 
04510b914a6c: Pull complete 
83ab617df7b4: Pull complete 
Digest: sha256:b8855dc848e2622653ab557d1ce2f4c34218a9380cceaa51ced85c5f3c8eb201
Status: Downloaded newer image for docker.io/ubuntu:14.04
Hello world

とりあえず動いた。

ユーザをdockerrootグループに追加

下記の話はあるみたいだが、とりあえずユーザをdockerグループに追加してみる。 qiita.com

ただし、dockerrootグループになっているようだった。

 cat /etc/group
root:x:0:
bin:x:1:
 :
略
 :
dockerroot:x:991:
# グループに追加
sudo gpasswd  -a username dockerroot

# 再実行(結局ダメだった)
docker run ubuntu:14.04 /bin/echo 'Hello world'
→
/usr/bin/docker-current: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.26/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See '/usr/bin/docker-current run --help'.

下記の話らしい。 qiita.com

ls -l /var/run/docker.sock
→
srw-rw---- 1 root root 0  5月 15 22:36 /var/run/docker.sock

sudo chown root:dockerroot /var/run/docker.sock 

再度、一般ユーザで実行

docker run ubuntu:14.04 /bin/echo 'Hello world'
→
/usr/bin/docker-current: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.26/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See '/usr/bin/docker-current run --help'.

同じエラーだった。なぜだろう・・・。 とりあえず、/var/run/docker.sockはdocker再起動後にはroot:rootに戻ってしまうことと、root:dockerrootになっていてもダメみたいだ。

qiita.com こんな記事を見つけたので、試してみたけど、結果は変わらずだった・・・。

sudo groupadd docker
sudo gpasswd -a username docker
sudo systemctl restart docker

できないと思ったけど、記事の末尾に書いてあるとおり、ログインし直したらdockerコマンドが使えるようになった!!

ついでに、docker-composeもインストールした。

sudo yum install docker-compose

SAKURAのVPSで設定したこと

前提

  • SAKURA用意されたCentOS7を使用
# 全体的なアップデート
yum clean all
yum update
reboot

SELinux

無効化するつもりだったけど、最初から無効化されていた

確認

getenforce
→ Disabled

一時的な無効化

setenforce 0
getenforce 

永続的な無効化

# vi /etc/selinux/config
SELINUX=disabled

FW

有効になっていたので、そのまま有効にする

systemctl status firewalld
→ Active: active (running)

ホスト名の変更

リブート後に有効になる。

hostnamectl set-hostname skr01
reboot

localeの確認と変更

# 確認
localectl status
   System Locale: LANG=C
       VC Keymap: jp106
      X11 Layout: jp

# 設定可能なロケールの一覧
localectl list-locales | grep ja
ja_JP
ja_JP.eucjp
ja_JP.ujis
ja_JP.utf8
japanese
japanese.euc

# 変更
localectl set-locale LANG=ja_JP.utf8

# 変更後の確認
localectl status 
   System Locale: LANG=ja_JP.utf8
       VC Keymap: jp106
      X11 Layout: jp

# source /etc/locale.conf で良いらしいけど、リブート
reboot

自動起動が有効になっているもののチェック

[root@skr01 ~]# systemctl list-unit-files -t service  | grep enabled
abrt-ccpp.service                             enabled 
abrt-oops.service                             enabled 
abrt-vmcore.service                           enabled 
abrt-xorg.service                             enabled 
abrtd.service                                 enabled 
atd.service                                   enabled 
auditd.service                                enabled 
autovt@.service                               enabled 
chronyd.service                               enabled 
crond.service                                 enabled 
dbus-org.fedoraproject.FirewallD1.service     enabled 
dbus-org.freedesktop.NetworkManager.service   enabled 
dbus-org.freedesktop.nm-dispatcher.service    enabled 
dmraid-activation.service                     enabled 
fail2ban.service                              enabled 
firewalld.service                             enabled 
getty@.service                                enabled 
irqbalance.service                            enabled 
libstoragemgmt.service                        enabled 
lvm2-monitor.service                          enabled 
mdmonitor.service                             enabled 
NetworkManager-dispatcher.service             enabled 
NetworkManager.service                        enabled 
postfix.service                               enabled 
rngd.service                                  enabled 
rsyslog.service                               enabled 
smartd.service                                enabled 
sshd.service                                  enabled 
sysstat.service                               enabled 
systemd-readahead-collect.service             enabled 
systemd-readahead-drop.service                enabled 
systemd-readahead-replay.service              enabled 
tuned.service                                 enabled 

不要サービスの停止

# 不要サービス(postfix)の停止と自動起動の停止
systemctl stop postfix.service
systemctl disable postfix.service
→ Removed symlink /etc/systemd/system/multi-user.target.wants/postfix.service.
# 結局、その後、再度可動させた。(Centos7ではrootにメールが来るらしい?ので念の為)
systemctl enable postfix.service
Created symlink from /etc/systemd/system/multi-user.target.wants/postfix.service to /usr/lib/systemd/system/postfix.service.
systemctl start postfix.service

一般ユーザを作成

useradd -G wheel hoge
passwd hoge

SSHのrootログインの無効化

PermitRootLogin no
systemctl restart sshd

ログの表示

journalctl -u sshd.service

SSHのポート変更とfirewalld

  • sshを削除して、20022を個別に開ける
  • IPv6未使用っぽいので、dhcpv6-clientも消して良さそうだけど、とりあえず保留した。
vi /etc/ssh/sshd_config

# ポート番号を20022にする(デフォルトは22)
Port 20022

# SSH2だけにする
Protocol 2

# rootでのログインを不可とする
PermitRootLogin no

# パスワードでのログインを許可する
PasswordAuthentication yes

# パスワードなしでのログインを不可とする
PermitEmptyPasswords no

# hoge というユーザだけログインを許可する
AllowUsers hoge

## 再起動
systemctl restart sshd
# 空いているサービスの確認
firewall-cmd --list-services 
→ dhcpv6-client ssh

# 空いているポートの確認
firewall-cmd --list-ports
→ なし。

# ポート20022を開ける(一時的な有効化)
firewall-cmd --add-port=20022/tcp
→ success

# ポート20022を開ける(永続的な有効化)
firewall-cmd --permanent --add-port=20022/tcp
→ success

# もともと有ったsshサービス(ポート22)を消す
firewall-cmd --permanent --remove-service=ssh
→ success

# firewalldを再起動
systemctl restart firewalld.service

# 空いているサービスの確認(sshが消えた)
firewall-cmd --list-services 
→ dhcpv6-client

# 空いているポートの確認(ssh用の20022が空いた)
firewall-cmd --list-ports 
→ 20022/tcp

SSHの接続を証明書に変更

# この作業はSSHでつなぎに行く一般ユーザでやる

# RSA&2048bitでも大丈夫っぽい。
ssh-keygen -t rsa -b 4096

# 移動
cd ~/.ssh

# 公開鍵を追記する
cat id_rsa.pub >> authorized_keys

# authorized_keys が 644 ではダメらしい。700に変更。なお変更しないと後術のエラーになる。
chmod 700 authorized_keys

メモ * ~/.ssh/にファイルが生成される * id_rsa * 秘密鍵(SSHクライアントに入れる) * パーミッションは600になっていた * id_rsa.pub * 公開鍵(サーバ側のauthorized_keysに追記する) * パーミッションは644になっていた

# 出力されるテキスト
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hoge/.ssh/id_rsa): 
Created directory '/home/hoge/.ssh'.
Enter passphrase (empty for no passphrase): パスワードを入れる
Enter same passphrase again: パスワードを入れる
Your identification has been saved in /home/hoge/.ssh/id_rsa.
Your public key has been saved in /home/hoge/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:hCK6wwxOcEU/ePWkVPUEHW1hQa1VOyIyOk+dIjxjvcc hoge@servername
The key's randomart image is:
+---[RSA 4096]----+
|   .o   o.o.oo+B*|
|   . o + +   oo *|
|. o o = oo.. ..* |
|.o . o.oo + o o .|
|o.     OS+ o     |
|*.    . B +      |
|o+       o E     |
| .        .      |
|                 |
+----[SHA256]-----+

メモ:入力したパスワードは、SSHクライアント側で秘密鍵を使用する際に入力することになる。

ハマったこと

秘密鍵/公開鍵でSSH接続するときのauthorized_keysのパーミッション

# エラーログ
journalctl -u sshd.service | tail
→ 5月 03 18:52:05 skr01 sshd[3600]: Authentication refused: bad ownership or modes for file /home/hoge/.ssh/authorized_keys