From afb5437254c7626b05fb6094220a709f646da149 Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Sat, 24 Jan 2026 13:08:05 +0900 Subject: [PATCH 1/2] feat: Add comprehensive Bash 10th line extraction tutorial with visualizations - Implement 3 correct solutions: sed, awk, and tail+head - Add detailed ASCII diagrams explaining each approach's execution flow - Include performance benchmarks (Runtime/Memory) for all methods - Document edge case handling for files with <10 lines - Fix incorrect head+tail approach and explain why it fails - Add comparison table of all solutions with pros/cons - Provide production-ready error handling examples Solutions: 1. sed -n '10p' (23ms, 3.85MB) - Most recommended 2. awk 'NR==10' (30ms, 3.92MB) - Most flexible 3. tail -n +10 | head -n 1 (27ms, 3.88MB) - Intuitive Key improvements: - Visual diagrams for each command's data flow - Correct handling of edge cases (files with <10 lines) - Explanation of why head -n 10 | tail -n 1 is incorrect - Japanese documentation for better accessibility --- .../Leetcode/195. Tenth Line/TenthLine.ipynb | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb diff --git a/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb b/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb new file mode 100644 index 00000000..be4446d5 --- /dev/null +++ b/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb @@ -0,0 +1,390 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a2d8d7c3", + "metadata": {}, + "source": [ + "# Bash Shellを用いた10行目の抽出問題\n", + "\n", + "この問題では、テキストファイルの10行目だけを出力する方法を解説します。3つの異なるアプローチと、それぞれの動作原理を図解付きで説明します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "896f0cc3", + "metadata": { + "vscode": { + "languageId": "shellscript" + } + }, + "outputs": [], + "source": [ + "#!/bin/bash\n", + "\n", + "# ========================================\n", + "# 解法1: sed を使用する方法\n", + "# ========================================\n", + "# Analyze Complexity\n", + "# Runtime 23 ms\n", + "# Beats 69.41%\n", + "# Memory 3.85 MB\n", + "# Beats 91.20%\n", + "\n", + "echo \"=== 解法1: sed ===\"\n", + "sed -n '10p' file.txt\n", + "\n", + "# 解説:\n", + "# -n : デフォルトの出力を抑制\n", + "# '10p' : 10行目のみを出力(print)\n", + "\n", + "\n", + "# ========================================\n", + "# 解法2: head と tail を組み合わせる方法\n", + "# ========================================\n", + "# Analyze Complexity\n", + "# Runtime 27 ms\n", + "# Beats 24.40%\n", + "# Memory 3.88 MB\n", + "# Beats 91.20%\n", + "\n", + "echo -e \"\\n=== 解法2: head + tail ===\"\n", + "tail -n +10 file.txt | head -n 1\n", + "\n", + "# 解説:\n", + "# head -n 10 : 最初の10行を取得\n", + "# tail -n 1 : その中から最後の1行(=10行目)を取得\n", + "\n", + "\n", + "# ========================================\n", + "# 解法3: awk を使用する方法\n", + "# ========================================\n", + "# Analyze Complexity\n", + "# Runtime 30 ms\n", + "# Beats 7.19%\n", + "# Memory 3.92 MB\n", + "# Beats 52.52%\n", + "\n", + "echo -e \"\\n=== 解法3: awk ===\"\n", + "awk 'NR==10' file.txt\n", + "\n", + "# 解説:\n", + "# NR : 現在の行番号(Number of Records)\n", + "# NR==10 : 行番号が10の時にその行を出力\n", + "\n", + "\n", + "# ========================================\n", + "# 補足: 10行未満の場合の処理\n", + "# ========================================\n", + "# Analyze Complexity\n", + "# Runtime 26 ms\n", + "# Beats 34.48%\n", + "# Memory 3.94 MB\n", + "# Beats 52.52%\n", + "\n", + "echo -e \"\\n=== 10行未満のファイルへの対応 ===\"\n", + "\n", + "# エラーチェック付きバージョン\n", + "if [ $(wc -l < file.txt) -ge 10 ]; then\n", + " sed -n '10p' file.txt\n", + "else\n", + " echo \"\"\n", + "fi" + ] + }, + { + "cell_type": "markdown", + "id": "b7bf7ad3", + "metadata": {}, + "source": [ + "## 詳細な図解と解説\n", + "\n", + "### **解法1: `sed` を使用する方法**\n", + "\n", + "```bash\n", + "sed -n '10p' file.txt\n", + "```\n", + "\n", + "**動作原理の図解:**\n", + "\n", + "```\n", + "file.txt sed -n '10p' 出力\n", + "┌─────────┐ ┌──────────────┐ ┌──────────┐\n", + "│ Line 1 │──┐ │ │ │ │\n", + "│ Line 2 │ │ │ -n: 自動出力 │ │ │\n", + "│ Line 3 │ │ │ OFF │ │ │\n", + "│ Line 4 │ ├──→│ │────────→│ │\n", + "│ Line 5 │ │ │ 10p: 10行目 │ │ │\n", + "│ Line 6 │ │ │ のみ出力 │ │ │\n", + "│ Line 7 │ │ │ │ │ │\n", + "│ Line 8 │ │ │ │ │ │\n", + "│ Line 9 │ │ │ │ │ │\n", + "│ Line 10 │──┘ │ ✓ │ │ Line 10 │\n", + "└─────────┘ └──────────────┘ └──────────┘\n", + "```\n", + "\n", + "**特徴:**\n", + "- `-n`: デフォルトの出力を抑制\n", + "- `10p`: 10行目に到達したら出力(print)\n", + "- **最もシンプルで効率的**\n", + "\n", + "---\n", + "\n", + "### **解法2: `head` と `tail` の組み合わせ**\n", + "\n", + "```bash\n", + "head -n 10 file.txt | tail -n 1\n", + "```\n", + "\n", + "**動作原理の図解:**\n", + "\n", + "```\n", + "file.txt head -n 10 tail -n 1 出力\n", + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", + "│ Line 1 │ │ Line 1 │ │ │ │ │\n", + "│ Line 2 │ │ Line 2 │ │ │ │ │\n", + "│ Line 3 │ │ Line 3 │ │ │ │ │\n", + "│ Line 4 │───→│ Line 4 │ │ │ │ │\n", + "│ Line 5 │ │ Line 5 │ │ │ │ │\n", + "│ Line 6 │ │ Line 6 │──────→│ 最後の1行 │───→│ Line 10 │\n", + "│ Line 7 │ │ Line 7 │ │ を取得 │ │ │\n", + "│ Line 8 │ │ Line 8 │ │ │ │ │\n", + "│ Line 9 │ │ Line 9 │ │ ✓ │ │ │\n", + "│ Line 10 │ │ Line 10 │ │ Line 10 │ │ │\n", + "│ Line 11 │ └──────────┘ └──────────┘ └──────────┘\n", + "│ Line 12 │ ↑ ここまで\n", + "└─────────┘\n", + "```\n", + "\n", + "**処理の流れ:**\n", + "1. `head -n 10`: 最初の10行を抽出\n", + "2. `tail -n 1`: その中から最後の1行(つまり元の10行目)を取得\n", + "\n", + "**特徴:**\n", + "- 直感的で理解しやすい\n", + "- 2つのコマンドを組み合わせるため、やや冗長\n", + "\n", + "---\n", + "\n", + "### **解法3: `awk` を使用する方法**\n", + "\n", + "```bash\n", + "awk 'NR==10' file.txt\n", + "```\n", + "\n", + "**動作原理の図解:**\n", + "\n", + "```\n", + "file.txt awk 'NR==10' 出力\n", + "┌─────────┐ ┌───────────────────┐ ┌──────────┐\n", + "│ Line 1 │ NR=1 ──→ │ NR==10? No │ │ │\n", + "│ Line 2 │ NR=2 ──→ │ NR==10? No │ │ │\n", + "│ Line 3 │ NR=3 ──→ │ NR==10? No │ │ │\n", + "│ Line 4 │ NR=4 ──→ │ NR==10? No │ │ │\n", + "│ Line 5 │ NR=5 ──→ │ NR==10? No │────────→│ │\n", + "│ Line 6 │ NR=6 ──→ │ NR==10? No │ │ │\n", + "│ Line 7 │ NR=7 ──→ │ NR==10? No │ │ │\n", + "│ Line 8 │ NR=8 ──→ │ NR==10? No │ │ │\n", + "│ Line 9 │ NR=9 ──→ │ NR==10? No │ │ │\n", + "│ Line 10 │ NR=10 ──→ │ NR==10? Yes! ✓ │ │ Line 10 │\n", + "└─────────┘ └───────────────────┘ └──────────┘\n", + "```\n", + "\n", + "**特徴:**\n", + "- `NR` (Number of Records): 現在の行番号を保持する変数\n", + "- 条件が真の時のみ行を出力(デフォルト動作)\n", + "- **テキスト処理に強力で柔軟性が高い**\n", + "\n", + "---\n", + "\n", + "## **10行未満のファイルへの対応**\n", + "\n", + "ファイルが10行未満の場合の動作比較:\n", + "\n", + "```\n", + "5行のファイルの場合:\n", + "\n", + "┌─────────┐\n", + "│ Line 1 │\n", + "│ Line 2 │\n", + "│ Line 3 │ ← 5行しかない\n", + "│ Line 4 │\n", + "│ Line 5 │\n", + "└─────────┘\n", + "\n", + "sed -n '10p' → 何も出力しない\n", + "head -n 10 | tail -n 1 → Line 5 を出力\n", + "awk 'NR==10' → 何も出力しない\n", + "```\n", + "\n", + "**エラーチェック付きバージョン:**\n", + "\n", + "```bash\n", + "# 行数を確認してから処理\n", + "if [ $(wc -l < file.txt) -ge 10 ]; then\n", + " sed -n '10p' file.txt\n", + "else\n", + " echo \"エラー: ファイルが10行未満です\" >&2\n", + "fi\n", + "```\n", + "\n", + "---\n", + "\n", + "## **各解法の比較表**\n", + "\n", + "| 解法 | コマンド | 長所 | 短所 | 10行未満の動作 |\n", + "|------|----------|------|------|----------------|\n", + "| **sed** | `sed -n '10p'` | シンプル、高速 | sed文法の知識が必要 | 何も出力しない |\n", + "| **head+tail** | `head -n 10 \\| tail -n 1` | 直感的 | 2つのプロセスが必要 | 最終行を出力 |\n", + "| **awk** | `awk 'NR==10'` | 柔軟性が高い | やや複雑 | 何も出力しない |\n", + "\n", + "---\n", + "\n", + "## **推奨される解法**\n", + "\n", + "**最もシンプルで効率的:** `sed -n '10p' file.txt`\n", + "\n", + "これが最も一般的で、多くの場合に推奨される方法です!\n", + "\n", + "ご指摘ありがとうございます!問題を特定しました。\n", + "\n", + "## **問題点の分析**\n", + "\n", + "ファイルが**9行しかない**場合、`head -n 10`は9行すべてを出力し、`tail -n 1`はその最後の行(9行目)を出力してしまいます。\n", + "\n", + "### **動作の図解**\n", + "\n", + "```\n", + "file.txt (9行) head -n 10 tail -n 1 出力\n", + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", + "│ 1 │ │ 1 │ │ │ │ │\n", + "│ 2 │ │ 2 │ │ │ │ │\n", + "│ 3 │ │ 3 │ │ │ │ │\n", + "│ 4 │──────→ 4 │ │ │ │ │\n", + "│ 5 │ │ 5 │──────→ 最後の1行 │────→ 9 │\n", + "│ 6 │ │ 6 │ │ を取得 │ │ │\n", + "│ 7 │ │ 7 │ │ ✓ │ │ ✗ 誤り! │\n", + "│ 8 │ │ 8 │ │ 9 │ │ │\n", + "│ 9 │ │ 9 │ │ │ │ │\n", + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", + " 9行しかない → 9行出力 → 9を出力\n", + "\n", + "期待される出力: 何も出力しない(10行目は存在しない)\n", + "実際の出力: 9\n", + "```\n", + "\n", + "## **正しい解法**## **3つの正しい解法**\n", + "\n", + "### **解法1: `sed -n '10p'`(最も推奨)**\n", + "\n", + "```bash\n", + "sed -n '10p' file.txt\n", + "```\n", + "\n", + "✅ **正しい動作:**\n", + "- 10行ある場合: 10行目を出力\n", + "- 9行しかない場合: 何も出力しない\n", + "\n", + "---\n", + "\n", + "### **解法2: `awk 'NR==10'`(推奨)**\n", + "\n", + "```bash\n", + "awk 'NR==10' file.txt\n", + "```\n", + "\n", + "✅ **正しい動作:**\n", + "- 10行ある場合: 10行目を出力\n", + "- 9行しかない場合: 何も出力しない\n", + "\n", + "---\n", + "\n", + "### **解法3: `tail -n +10 | head -n 1`(推奨)**\n", + "\n", + "```bash\n", + "tail -n +10 file.txt | head -n 1\n", + "```\n", + "\n", + "**動作原理の図解:**\n", + "\n", + "```\n", + "file.txt (9行) tail -n +10 head -n 1 出力\n", + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", + "│ 1 │ │ │ │ │ │ │\n", + "│ 2 │ │ │ │ │ │ │\n", + "│ 3 │ │ │ │ │ │ │\n", + "│ 4 │ │ │ │ │ │ │\n", + "│ 5 │ │ (空) │──────→ (空) │────→ (空) │\n", + "│ 6 │ │ │ │ │ │ │\n", + "│ 7 │ │ │ │ │ │ │\n", + "│ 8 │ │ │ │ │ │ │\n", + "│ 9 │ │ │ │ │ │ │\n", + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", + " 10行目以降がない → 何も出力しない ✓\n", + "\n", + "\n", + "file.txt (10行) tail -n +10 head -n 1 出力\n", + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", + "│ 1 │ │ │ │ │ │ │\n", + "│ 2 │ │ │ │ │ │ │\n", + "│ 3 │ │ │ │ ✓ │ │ │\n", + "│ 4 │ │ 10 │──────→ 10 │────→ 10 │\n", + "│ 5 │ │ │ │ │ │ │\n", + "│ 6 │ │ │ │ │ │ │\n", + "│ 7 │ │ │ │ │ │ │\n", + "│ 8 │ │ │ │ │ │ │\n", + "│ 9 │ │ │ │ │ │ │\n", + "│ 10 │ │ │ │ │ │ │\n", + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", + "```\n", + "\n", + "✅ **正しい動作:**\n", + "- `tail -n +10`: 10行目から最後まで取得(+10は「10行目から」の意味)\n", + "- `head -n 1`: その最初の1行を取得\n", + "\n", + "---\n", + "\n", + "## **なぜ `head -n 10 | tail -n 1` は間違いなのか**\n", + "\n", + "```\n", + "❌ 間違った解法の動作:\n", + "\n", + "9行のファイル:\n", + "1. head -n 10 → 9行すべてを出力(10行ないので9行しか取れない)\n", + "2. tail -n 1 → その最後の1行 = 9行目を出力\n", + "結果: 9 が出力される(誤り)\n", + "\n", + "10行のファイル:\n", + "1. head -n 10 → 10行を出力\n", + "2. tail -n 1 → その最後の1行 = 10行目を出力\n", + "結果: 10 が出力される(正しい)\n", + "```\n", + "\n", + "## **正解のまとめ**\n", + "\n", + "LeetCode/オンラインジャッジで正解する解法:\n", + "\n", + "```bash\n", + "# 解法1(最もシンプル)\n", + "sed -n '10p' file.txt\n", + "\n", + "# 解法2(汎用性が高い)\n", + "awk 'NR==10' file.txt\n", + "\n", + "# 解法3(tailの+記法を使用)\n", + "tail -n +10 file.txt | head -n 1\n", + "```\n", + "\n", + "これらはすべて、ファイルが10行未満の場合は**何も出力しない**ため、テストケースをすべてパスします!" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From cb863a940a2a01eeb3fc008cda08580014a26b0f Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Sat, 24 Jan 2026 14:10:45 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(shell):=20192.=20Word=20Frequency?= =?UTF-8?q?=20-=20=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E5=85=A8=E9=9D=A2=E6=94=B9=E8=A8=82=E3=81=A8=E6=9C=AA=E3=82=B3?= =?UTF-8?q?=E3=83=9F=E3=83=83=E3=83=88=E5=88=86=E3=81=AE=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 主な変更: 192. Word Frequency ### 構造改善 - 重複していた説明セクション(修正方針1,2)を統合 - 問題概要 → 解答 → 詳細 → 応用 の論理的な流れに再構成 - ステップバイステップの処理説明を視覚化 ### Mermaid図の修正 - 安全な記法で統一(HTML エンティティ使用) - 特殊文字のエスケープ処理を明確化 - [ ] { } による角かっこ・波かっこの適切な表現 ### 追加コンテンツ - よくある質問(FAQ)セクション - tr -s オプションの説明 - LC_ALL=C の使用理由 - uniq -c の出力形式 - 応用例 - 圧縮ファイルの処理 - ストリーム処理 - 大文字小文字を区別しない処理 - Mermaid 記法の注意点とベストプラクティス ### 技術的?## 技術的?## 技術的?## 技術的?## 洰### 技???フォーマンス最適化のポイント追加 - ロケール設定の重要性を明記 - 代替解法(awk メイン)の追加 ## その他の変更 - 未コミットファイルの整理 - ドキュメントの一貫性向上 --- .../192. Word Frequency/WordFrequency.ipynb | 363 +++++++++++++++++ .../192. Word Frequency/WordFrequency.md | 362 ----------------- .../ValidPhoneNumbers.ipynb | 312 ++++++++------- .../194. Transpose File/TransposeFile.ipynb | 365 +++++++++++++++--- .../Leetcode/195. Tenth Line/TenthLine.ipynb | 314 ++++++++------- 5 files changed, 997 insertions(+), 719 deletions(-) create mode 100644 Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.ipynb delete mode 100644 Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.md diff --git a/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.ipynb b/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.ipynb new file mode 100644 index 00000000..f2887c90 --- /dev/null +++ b/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.ipynb @@ -0,0 +1,363 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "43f342e3", + "metadata": {}, + "source": [ + "# 192. Word Frequency - Bash解法\n", + "\n", + "## 問題概要\n", + "\n", + "テキストファイル `words.txt` から各単語の出現頻度を集計し、頻度の降順で出力する問題です。\n", + "\n", + "---\n", + "\n", + "## 解答(スクリプト版)\n", + "\n", + "`wordfreq.sh`(Bash, POSIX ツールのみ)\n", + "\n", + "```bash\n", + "#!/usr/bin/env bash\n", + "set -euo pipefail\n", + "\n", + "# 使い方: ./wordfreq.sh [path/to/words.txt]\n", + "# 引数が未指定なら ./words.txt を読む\n", + "input=\"${1:-words.txt}\"\n", + "\n", + "# 1) 全ての空白(スペース/タブ/改行など)を改行にし、連続空白は1つに圧縮\n", + "# 2) ソート\n", + "# 3) uniq -c で頻度集計\n", + "# 4) 頻度(第1列)で数値降順ソート\n", + "# 5) \"単語 頻度\" の並びに整形\n", + "LC_ALL=C tr -s '[:space:]' '\\n' < \"$input\" \\\n", + " | sort \\\n", + " | uniq -c \\\n", + " | sort -nr \\\n", + " | awk '{print $2, $1}'\n", + "```\n", + "\n", + "### 実行方法\n", + "\n", + "```bash\n", + "chmod +x wordfreq.sh\n", + "./wordfreq.sh # カレントの words.txt を集計\n", + "# もしくは\n", + "./wordfreq.sh /path/to/words.txt\n", + "```\n", + "\n", + "---\n", + "\n", + "## 解答(パイプのみの1行版)\n", + "\n", + "```bash\n", + "LC_ALL=C tr -s '[:space:]' '\\n' < words.txt | sort | uniq -c | sort -nr | awk '{print $2, $1}'\n", + "```\n", + "\n", + "---\n", + "\n", + "## 入出力例\n", + "\n", + "### 入力 (`words.txt`)\n", + "\n", + "```text\n", + "the day is sunny the the\n", + "the sunny is is\n", + "```\n", + "\n", + "### 出力\n", + "\n", + "```text\n", + "the 4\n", + "is 3\n", + "sunny 2\n", + "day 1\n", + "```\n", + "\n", + "---\n", + "\n", + "## 処理フロー図解\n", + "\n", + "```mermaid\n", + "flowchart LR\n", + " A[\"words.txt
入力ファイル\"] --> B[\"tr -s [:space:] \\\\n
全ての空白→改行
連続空白を1つに圧縮\"]\n", + " B --> C[\"sort
辞書順整列\"]\n", + " C --> D[\"uniq -c
連続同一語をカウント\"]\n", + " D --> E[\"sort -nr
頻度で降順ソート\"]\n", + " E --> F[\"awk {print $2, $1}
「単語 頻度」形式に整形\"]\n", + " F --> G[\"結果出力\"]\n", + "```\n", + "\n", + "---\n", + "\n", + "## ステップ別の処理詳細\n", + "\n", + "### 入力データ\n", + "\n", + "```text\n", + "the day is sunny the the\n", + "the sunny is is\n", + "```\n", + "\n", + "### ステップ1: `tr -s '[:space:]' '\\n'`\n", + "\n", + "全ての空白文字(スペース・タブ・改行)を改行に変換し、連続する空白は1つに圧縮します。\n", + "\n", + "```text\n", + "the\n", + "day\n", + "is\n", + "sunny\n", + "the\n", + "the\n", + "the\n", + "sunny\n", + "is\n", + "is\n", + "```\n", + "\n", + "### ステップ2: `sort`\n", + "\n", + "単語を辞書順に整列します(`uniq -c` は連続した同一行のみカウントするため必須)。\n", + "\n", + "```text\n", + "day\n", + "is\n", + "is\n", + "is\n", + "sunny\n", + "sunny\n", + "the\n", + "the\n", + "the\n", + "the\n", + "```\n", + "\n", + "### ステップ3: `uniq -c`\n", + "\n", + "連続する同一単語をカウントします。\n", + "\n", + "```text\n", + " 1 day\n", + " 3 is\n", + " 2 sunny\n", + " 4 the\n", + "```\n", + "\n", + "### ステップ4: `sort -nr`\n", + "\n", + "頻度(第1列)で数値降順ソートします。\n", + "\n", + "```text\n", + " 4 the\n", + " 3 is\n", + " 2 sunny\n", + " 1 day\n", + "```\n", + "\n", + "### ステップ5: `awk '{print $2, $1}'`\n", + "\n", + "「単語 頻度」の形式に整形します。\n", + "\n", + "```text\n", + "the 4\n", + "is 3\n", + "sunny 2\n", + "day 1\n", + "```\n", + "\n", + "---\n", + "\n", + "## アルゴリズムの解説\n", + "\n", + "### なぜこの順番なのか?\n", + "\n", + "1. **`tr` で正規化**\n", + " - 様々な空白文字(スペース・タブ・改行)を統一的に処理\n", + " - 連続空白の圧縮により空行を防止\n", + "\n", + "2. **最初の `sort` が必須**\n", + " - `uniq -c` は**連続した**同一行のみカウント\n", + " - 事前に整列することで同じ単語を隣接させる\n", + "\n", + "3. **`uniq -c` で集計**\n", + " - 連続する同一単語の出現回数をカウント\n", + " - 出力形式: `<頻度> <単語>`\n", + "\n", + "4. **`sort -nr` で降順**\n", + " - `-n`: 数値としてソート\n", + " - `-r`: 降順(reverse)\n", + "\n", + "5. **`awk` で整形**\n", + " - 列の順序を入れ替え: `$2 $1` → `<単語> <頻度>`\n", + "\n", + "---\n", + "\n", + "## 代替解法(awk メイン)\n", + "\n", + "`awk` の連想配列を使った方法:\n", + "\n", + "```bash\n", + "awk '{for(i=1;i<=NF;i++) c[$i]++} END{for(w in c) print w, c[w]}' words.txt \\\n", + " | LC_ALL=C sort -k2,2nr\n", + "```\n", + "\n", + "### 処理の流れ\n", + "\n", + "1. `awk` で各単語をカウント\n", + " - `NF`: 行内のフィールド数(空白区切り)\n", + " - `c[$i]++`: 連想配列でカウント\n", + "\n", + "2. `END` ブロックで出力\n", + " - `for(w in c)`: 全ての単語をループ\n", + " - `print w, c[w]`: 単語と頻度を出力\n", + "\n", + "3. `sort -k2,2nr` で頻度降順ソート\n", + " - `-k2,2`: 第2列(頻度)でソート\n", + " - `n`: 数値ソート\n", + " - `r`: 降順\n", + "\n", + "---\n", + "\n", + "## パフォーマンス最適化のポイント\n", + "\n", + "### 1. ロケール設定\n", + "\n", + "```bash\n", + "LC_ALL=C\n", + "```\n", + "\n", + "- C ロケールを使用することで `sort` が高速化\n", + "- バイト単位の比較により安定した動作\n", + "\n", + "### 2. 空行の除去(必要に応じて)\n", + "\n", + "`tr -s` を使っていれば基本的に不要ですが、念のため:\n", + "\n", + "```bash\n", + "... | grep -v '^$' | ...\n", + "```\n", + "\n", + "### 3. 入力ファイルの柔軟な指定\n", + "\n", + "スクリプト版では引数でファイルパスを指定可能:\n", + "\n", + "```bash\n", + "input=\"${1:-words.txt}\"\n", + "```\n", + "\n", + "---\n", + "\n", + "## 応用例\n", + "\n", + "### 圧縮ファイルの処理\n", + "\n", + "```bash\n", + "zcat compressed.txt.gz | tr -s '[:space:]' '\\n' | sort | uniq -c | sort -nr | awk '{print $2, $1}'\n", + "```\n", + "\n", + "### ストリーム処理\n", + "\n", + "```bash\n", + "curl -s https://example.com/text.txt | tr -s '[:space:]' '\\n' | sort | uniq -c | sort -nr | awk '{print $2, $1}'\n", + "```\n", + "\n", + "### 大文字小文字を区別しない\n", + "\n", + "```bash\n", + "LC_ALL=C tr -s '[:space:]' '\\n' < words.txt \\\n", + " | tr '[:upper:]' '[:lower:]' \\\n", + " | sort \\\n", + " | uniq -c \\\n", + " | sort -nr \\\n", + " | awk '{print $2, $1}'\n", + "```\n", + "\n", + "---\n", + "\n", + "## よくある質問\n", + "\n", + "### Q1: `tr -s` の `-s` オプションは何をする?\n", + "\n", + "**A:** `-s` (squeeze) は連続する文字を1つに圧縮します。\n", + "\n", + "```bash\n", + "# 例: 連続するスペースを1つに\n", + "echo \"a b c\" | tr -s ' '\n", + "# 出力: a b c\n", + "```\n", + "\n", + "### Q2: なぜ `LC_ALL=C` を使うのか?\n", + "\n", + "**A:** \n", + "- ロケール依存の文字比較を避ける\n", + "- バイト単位の比較で高速化\n", + "- 環境による動作の違いを防ぐ\n", + "\n", + "### Q3: `uniq -c` の出力形式は?\n", + "\n", + "**A:** `<頻度><スペース><単語>` の形式で出力されます。\n", + "\n", + "```text\n", + " 4 the\n", + " 3 is\n", + "```\n", + "\n", + "先頭にスペースが入るため、`awk` で列を入れ替える際は `$1` が頻度、`$2` が単語になります。\n", + "\n", + "---\n", + "\n", + "## Mermaid図の注意点\n", + "\n", + "Mermaid でコマンドを含むラベルを書く際の安全な記法:\n", + "\n", + "### 特殊文字のエスケープ\n", + "\n", + "- 角かっこ `[` `]` → `[` `]`\n", + "- 波かっこ `{` `}` → `{` `}`\n", + "- バックスラッシュ `\\` → `\\\\`\n", + "- シングルクォート `'` → `'`(必要な場合)\n", + "\n", + "### 推奨記法\n", + "\n", + "```mermaid\n", + "flowchart LR\n", + " A[\"ノード名\"] --> B[\"コマンド
説明文\"]\n", + "```\n", + "\n", + "- ラベル全体を二重引用符 `[\"...\"]` で囲む\n", + "- コマンド部分は `` タグで囲む\n", + "- 改行は `
` を使用\n", + "\n", + "---\n", + "\n", + "## まとめ\n", + "\n", + "この問題の解法ポイント:\n", + "\n", + "1. **`tr`** で空白を正規化\n", + "2. **`sort`** で同一単語を隣接させる\n", + "3. **`uniq -c`** で頻度をカウント\n", + "4. **`sort -nr`** で頻度降順ソート\n", + "5. **`awk`** で出力形式を整形\n", + "\n", + "シンプルな POSIX ツールの組み合わせで効率的に処理できます。\n", + "\n", + "主な改善点:\n", + "1. 重複セクションを完全に削除\n", + "2. 構造を論理的に整理(問題→解答→詳細→応用)\n", + "3. Mermaid図を1つに統一(安全な記法を使用)\n", + "4. よくある質問セクションを追加\n", + "5. 応用例を充実\n", + "6. Mermaid記法の注意点を最後にまとめ" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.md b/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.md deleted file mode 100644 index e433b9f8..00000000 --- a/Shell/Bash/Leetcode/192. Word Frequency/WordFrequency.md +++ /dev/null @@ -1,362 +0,0 @@ -# 解答(スクリプト版) - -`wordfreq.sh`(Bash, POSIX ツールのみ) - -```bash -#!/usr/bin/env bash -set -euo pipefail - -# 使い方: ./wordfreq.sh [path/to/words.txt] -# 引数が未指定なら ./words.txt を読む -input="${1:-words.txt}" - -# 1) 全ての空白(スペース/タブ/改行など)を改行にし、連続空白は1つに圧縮 -# 2) ソート -# 3) uniq -c で頻度集計 -# 4) 頻度(第1列)で数値降順ソート -# 5) "単語 頻度" の並びに整形 -LC_ALL=C tr -s '[:space:]' '\n' < "$input" \ - | sort \ - | uniq -c \ - | sort -nr \ - | awk '{print $2, $1}' -``` - -実行権限を付与して実行: - -```bash -chmod +x wordfreq.sh -./wordfreq.sh # カレントの words.txt を集計 -# もしくは -./wordfreq.sh /path/to/words.txt -``` - ---- - -## 解答(パイプのみの 1 行) - -```bash -LC_ALL=C tr -s '[:space:]' '\n' < words.txt | sort | uniq -c | sort -nr | awk '{print $2, $1}' -``` - ---- - -## 出力例 - -`words.txt` が次の内容のとき: - -```text -the day is sunny the the -the sunny is is -``` - -どちらの方法でも: - -```text -the 4 -is 3 -sunny 2 -day 1 -``` - ---- - -## 図解:処理の流れ - -```mermaid -flowchart LR - A[words.txt
入力ファイル] --> B["tr -s '[:space:]' '\\n'
全ての空白→改行 / 連続を1つに圧縮"] - B --> C["sort
単語を辞書順に並べる"] - C --> D["uniq -c
連続する同一単語をカウント"] - D --> E["sort -nr
頻度(数値)で降順ソート"] - E --> F["awk '{print $2, $1}'
「単語 頻度」に整形"] - F --> G[結果] -``` - ---- - -## ステップ別のミニ実演(変換のイメージ) - -入力: - -```text -the day is sunny the the -the sunny is is -``` - -1. **tr -s '[:space:]' '\n'** - 全ての空白を改行に(重複空白は圧縮): - -```text -the -day -is -sunny -the -the -the -sunny -is -is -``` - -## 2. **sort**(辞書順) - -```text -day -is -is -is -sunny -sunny -the -the -the -the -``` - -## 3. **uniq -c**(連続同一語を数える) - -```text - 1 day - 3 is - 2 sunny - 4 the -``` - -## 4. **sort -nr**(頻度降順) - -```text - 4 the - 3 is - 2 sunny - 1 day -``` - -## 5. **awk '{print $2, $1}'**(「単語 頻度」に整形) - -```text -the 4 -is 3 -sunny 2 -day 1 -``` - ---- - -## なぜこの順番? - -- `uniq -c` は**連続**した同一行しか数えられないため、まず `sort` で同じ単語を隣接させます。 -- `tr -s '[:space:]' '\n'` を先に使うことで、スペース・タブ・改行など**あらゆる空白**を 1 語 1 行に正規化できます。 -- 最終的に `sort -nr` で頻度を**数値降順**にし、`awk` で「単語 頻度」に整えます。 - ---- - -## 代替ワンライナー(awk メイン) - -`awk` でカウントしてから、頻度で降順ソートする方法もあります。 -(大量データ時のパイプ構成はお好みで) - -```bash -awk '{for(i=1;i<=NF;i++) c[$i]++} END{for(w in c) print w, c[w]}' words.txt \ - | LC_ALL=C sort -k2,2nr -``` - -- `NF` は行内のフィールド数(空白区切り) -- 連想配列 `c[word]` にカウント -- END で「単語 頻度」を出力し、`sort -k2,2nr` で第 2 列(頻度)を降順ソート - ---- - -## 小さな TIPS - -- **ロケール固定**: `LC_ALL=C` を付けると `sort` が速く・安定します。 -- **空行ケア**: `tr -s` を使っていれば空行は基本的に出ませんが、念のためはさむなら - `... | grep -v '^$' | ...` を `sort` の前に入れても OK です。 -- **入力ファイル指定**: スクリプト版は引数でファイルを差し替え可能にしています。 - -必要なら、`zcat ... | tr ... | ...` のように圧縮ファイルやストリームにも簡単に応用できます。 - -## 修正点 - -原因は、**ノードラベル内に Mermaid の構文と衝突する文字(`[ ] ' \ :` など)が“そのまま”入っている**ためです。 -あなたの行には `B[tr -s '[:space:]' '\n'
...]` とありますが、 -ラベル中の `[` と `]`(`[:space:]` の部分)が **ノードの区切り `[...]`** と誤解され、 -さらに直後のシングルクォート `'` でパーサがこけています(`got 'SQS'` は Single Quote String を示唆)。 - -## 修正方針(どれか or 併用)1 - -1. **ラベルを二重引用符で囲む**:`id[" ... "]` -2. **ラベル内の角かっこ `[` `]` を HTML エンティティに置換**:`[` と `]` -3. **必要に応じてクォートやバックスラッシュをエスケープ/置換**:`'` → `'`、`\n` → `\\n` など -4. **長いコマンドはコード風に**(バッククォート)**や改行 `
` を併用** - ---- - -## 直した例(あなたのフローをそのまま修正) - -```mermaid -flowchart LR - A[words.txt
入力ファイル] --> B["tr -s '[:space:]' '\\n'
全ての空白→改行 / 連続を1つに圧縮"] - B --> C["sort
単語を辞書順に並べる"] - C --> D["uniq -c
連続する同一単語をカウント"] - D --> E["sort -nr
頻度(数値)で降順ソート"] - E --> F["awk '{print $2, $1}'
「単語 頻度」に整形"] - F --> G[結果] -``` - -### ポイント 1 - -- `B[...]` を `B["..."]` に変更(**二重引用符**でラベル全体を囲む) -- `[:space:]` の角かっこを **`[` と `]`** に置換 -- シングルクォート `'` は **`'`** に(中でさらに `'` を使いたいときの衝突回避) -- `\n` は **`\\n`** に(バックスラッシュをエスケープ) -- `
` はそのままで OK(HTML ラベルは許容されます) - ---- - -## もう少し読みやすくする代替(コマンド部分をコード表記)1 - -```mermaid -flowchart LR - A[words.txt
入力ファイル] --> B["`tr -s [:space:] \\n`
全ての空白→改行 / 連続は1つに圧縮"] - B --> C["`sort`
辞書順整列"] - C --> D["`uniq -c`
連続同一語をカウント"] - D --> E["`sort -nr`
頻度で降順"] - E --> F["`awk '{print $2, $1}'`
出力整形(単語 頻度)"] - F --> G[結果] -``` - -> バッククォート `` `...` `` で**コード風**に包むと、特殊文字の衝突が起きにくく、可読性も上がります。 - ---- - -## 安全な書き方テンプレ 1 - -- ラベルが記号を含むときは **常に `id["..."]`** を使う -- 角かっこは **`[` `]`** に -- シングルクォートは **`'`**、ダブルクォートは **`"`** -- バックスラッシュは **`\\`** -- 改行は **`
`** を使う(Mermaid は HTML ラベル可) - -この要領で直すと、パースエラーは解消されます。 - -原因は、**ノードラベル内に Mermaid の構文と衝突する文字(`[ ] ' \ :` など)が“そのまま”入っている**ためです。 -あなたの行には `B[tr -s '[:space:]' '\n'
...]` とありますが、 -ラベル中の `[` と `]`(`[:space:]` の部分)が **ノードの区切り `[...]`** と誤解され、 -さらに直後のシングルクォート `'` でパーサがこけています(`got 'SQS'` は Single Quote String を示唆)。 - -## 修正方針(どれか or 併用)2 - -1. **ラベルを二重引用符で囲む**:`id[" ... "]` -2. **ラベル内の角かっこ `[` `]` を HTML エンティティに置換**:`[` と `]` -3. **必要に応じてクォートやバックスラッシュをエスケープ/置換**:`'` → `'`、`\n` → `\\n` など -4. **長いコマンドはコード風に**(バッククォート)**や改行 `
` を併用** - ---- - -## 直した例(あなたのフローをそのまま修正)2 - -```mermaid -flowchart LR - A[words.txt
入力ファイル] --> B["tr -s '[:space:]' '\\n'
全ての空白→改行 / 連続を1つに圧縮"] - B --> C["sort
単語を辞書順に並べる"] - C --> D["uniq -c
連続する同一単語をカウント"] - D --> E["sort -nr
頻度(数値)で降順ソート"] - E --> F["awk '{print $2, $1}'
「単語 頻度」に整形"] - F --> G[結果] -``` - -### ポイント 2 - -- `B[...]` を `B["..."]` に変更(**二重引用符**でラベル全体を囲む) -- `[:space:]` の角かっこを **`[` と `]`** に置換 -- シングルクォート `'` は **`'`** に(中でさらに `'` を使いたいときの衝突回避) -- `\n` は **`\\n`** に(バックスラッシュをエスケープ) -- `
` はそのままで OK(HTML ラベルは許容されます) - ---- - -## もう少し読みやすくする代替(コマンド部分をコード表記)2 - -```mermaid -flowchart LR - A[words.txt
入力ファイル] --> B["`tr -s [:space:] \\n`
全ての空白→改行 / 連続は1つに圧縮"] - B --> C["`sort`
辞書順整列"] - C --> D["`uniq -c`
連続同一語をカウント"] - D --> E["`sort -nr`
頻度で降順"] - E --> F["`awk '{print $2, $1}'`
出力整形(単語 頻度)"] - F --> G[結果] -``` - -> バッククォート `` `...` `` で**コード風**に包むと、特殊文字の衝突が起きにくく、可読性も上がります。 - ---- - -## 安全な書き方テンプレ 2 - -- ラベルが記号を含むときは **常に `id["..."]`** を使う -- 角かっこは **`[` `]`** に -- シングルクォートは **`'`**、ダブルクォートは **`"`** -- バックスラッシュは **`\\`** -- 改行は **`
`** を使う(Mermaid は HTML ラベル可) - -この要領で直すと、パースエラーは解消されます。 - -発生原因はまだ**ソースにスマート置換で混入した非 ASCII 文字**(合字やスマート引用符など)が残っていること+**Mermaid のラベル内での特殊記号衝突**です。 -安全策として「完全 ASCII・最小限の記号・HTML エンティティで角かっこを逃がす・バッククォート不使用」の版に差し替えてください。下のどれかを**そのままコピペ**で動きます。 - ---- - -## 安全版その 1(ASCII のみ・角かっこを HTML エンティティ化・バッククォート不使用) - -```mermaid -flowchart LR - A["words.txt
入力ファイル"] --> B["tr -s [:space:] \\n
全ての空白→改行 / 連続は1つに圧縮"] - B --> C["sort
辞書順整列"] - C --> D["uniq -c
連続同一語をカウント"] - D --> E["sort -nr
頻度で降順"] - E --> F["awk {print $2, $1}
出力整形(単語 頻度)"] - F --> G["結果"] -``` - -ポイント - -- `[:space:]` の角かっこは `[` と `]` -- 改行リテラルは `\\n` -- `awk '{print $2, $1}'` は衝突回避のため **波かっこをエンティティ化**(`{` `}`)し、**シングルクォートを排除**しました(見た目の違いだけで意味説明には十分) - ---- - -## 安全版その 2(コマンドを `` で表示・記号衝突をさらに回避) - -```mermaid -flowchart LR - A["words.txt
入力ファイル"] --> B["tr -s [:space:] \\n
全ての空白→改行 / 連続は1つに圧縮"] - B --> C["sort
辞書順整列"] - C --> D["uniq -c
連続同一語をカウント"] - D --> E["sort -nr
頻度で降順"] - E --> F["awk {print $2, $1}
出力整形(単語 頻度)"] - F --> G["結果"] -``` - -> `...` を使うとバッククォート不要で、Markdown/レンダラーによる再変換の影響を受けにくくなります。 - ---- - -## それでも直らない場合のチェックリスト - -1. **貼り付けは必ずプレーンテキスト**で - - ブラウザやエディタの「Paste and Match Style / Paste as plain text」を使う - -2. **エディタ設定**(例:VS Code) - - `"editor.fontLigatures": false`(合字を無効) - - Markdown 拡張の **スマート引用符/全角置換を無効** に - -3. **ファイルエンコード**:UTF-8(BOM なし推奨) -4. **問題記号の回避** - - 角かっこは `[` `]` - - バックスラッシュは必ず `\\` と二重 - - シングル/ダブルクォートは極力使わないか、必要なら `'` / `"` - -このどちらかの安全版を使えば、`Lexical error on line 2. Unrecognized text.` は解消されるはずです。 diff --git a/Shell/Bash/Leetcode/193. Valid Phone Numbers/ValidPhoneNumbers.ipynb b/Shell/Bash/Leetcode/193. Valid Phone Numbers/ValidPhoneNumbers.ipynb index a5d8da92..24874f95 100644 --- a/Shell/Bash/Leetcode/193. Valid Phone Numbers/ValidPhoneNumbers.ipynb +++ b/Shell/Bash/Leetcode/193. Valid Phone Numbers/ValidPhoneNumbers.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e7bafc2d", + "id": "e5ef6417", "metadata": {}, "source": [ "# 電話番号フィルタリング問題の解説\n", @@ -17,20 +17,9 @@ "\n", "ここで `x` は数字(0-9)を表します。\n", "\n", - "## 解決策" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "39aacb70", - "metadata": { - "vscode": { - "languageId": "powershell" - } - }, - "outputs": [], - "source": [ + "## 解決策\n", + "\n", + "```bash\n", "#!/bin/bash\n", "\n", "# Solution 1: Using grep with extended regex\n", @@ -41,7 +30,9 @@ "# Beats 84.10%\n", "\n", "grep -E '^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$' file.txt\n", + "```\n", "\n", + "```bash\n", "# Solution 2: Using sed (alternative)\n", "# Analyze Complexity\n", "# Runtime 65 ms\n", @@ -50,7 +41,9 @@ "# Beats 84.10%\n", "\n", "sed -n -E '/^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$/p' file.txt\n", + "```\n", "\n", + "```bash\n", "# Solution 3: Using awk (alternative)\n", "# Analyze Complexity\n", "# Runtime 71 ms\n", @@ -58,14 +51,9 @@ "# Memory 3.84 MB\n", "# Beats 3.13%\n", "\n", - "awk '/^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$/' file.txt" - ] - }, - { - "cell_type": "markdown", - "id": "e5ef6417", - "metadata": {}, - "source": [ + "awk '/^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$/' file.txt\n", + "```\n", + "\n", "## 最もシンプルな解答\n", "\n", "```bash\n", @@ -78,143 +66,191 @@ "\n", "### 1. 正規表現パターンの構造\n", "\n", - "```\n", - "^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$\n", + "```mermaid\n", + "graph TD\n", + " A[\"正規表現全体
^...OR...$\"] --> B[\"行頭アンカー ^\"]\n", + " A --> C[\"OR演算子 |\"]\n", + " A --> D[\"行末アンカー $\"]\n", + " \n", + " C --> E[\"パターン1
xxx-xxx-xxxx\"]\n", + " C --> F[\"パターン2
(xxx) xxx-xxxx\"]\n", + " \n", + " E --> E1[\"[0-9]{3}\"]\n", + " E --> E2[\"-\"]\n", + " E --> E3[\"[0-9]{3}\"]\n", + " E --> E4[\"-\"]\n", + " E --> E5[\"[0-9]{4}\"]\n", + " \n", + " F --> F1[\"\\(\"]\n", + " F --> F2[\"[0-9]{3}\"]\n", + " F --> F3[\"\\)\"]\n", + " F --> F4[\"スペース\"]\n", + " F --> F5[\"[0-9]{3}\"]\n", + " F --> F6[\"-\"]\n", + " F --> F7[\"[0-9]{4}\"]\n", + " \n", + " style A fill:#e1f5ff\n", + " style E fill:#c8e6c9\n", + " style F fill:#fff9c4\n", "```\n", "\n", - "この正規表現を分解して理解しましょう:\n", - "\n", - "#### **全体構造**\n", - "```\n", - " ^ $\n", - " | |\n", - " 行頭 OR演算子 行末\n", - " ┌──────────────────┴──────────────────┐\n", - " | |\n", - " パターン1 パターン2\n", - " xxx-xxx-xxxx (xxx) xxx-xxxx\n", + "#### **パターン1の詳細構造**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[\"[0-9]{3}\"] -->|\"例: 987\"| B[\"-\"]\n", + " B --> C[\"[0-9]{3}\"]\n", + " C -->|\"例: 123\"| D[\"-\"]\n", + " D --> E[\"[0-9]{4}\"]\n", + " E -->|\"例: 4567\"| F[\"結果: 987-123-4567\"]\n", + " \n", + " style A fill:#ffcdd2\n", + " style C fill:#f8bbd0\n", + " style E fill:#e1bee7\n", + " style F fill:#c5cae9\n", "```\n", "\n", - "#### **パターン1: `[0-9]{3}-[0-9]{3}-[0-9]{4}`**\n", - "\n", - "```\n", - " [0-9]{3} - [0-9]{3} - [0-9]{4}\n", - " │ │ │ │ │\n", - " 3桁の数字 ハイフン 3桁の数字 ハイフン 4桁の数字\n", - " │ │ │\n", - " 987 123 4567\n", - "\n", - "例: 987-123-4567\n", - "```\n", - "\n", - "#### **パターン2: `\\([0-9]{3}\\) [0-9]{3}-[0-9]{4}`**\n", - "\n", - "```\n", - " \\( [0-9]{3} \\) スペース [0-9]{3} - [0-9]{4}\n", - " │ │ │ │ │ │ │\n", - " 左カッコ 3桁 右カッコ 空白 3桁 ハイフン 4桁\n", - " │ │ │ │ │\n", - " ( 123 ) 456 7890\n", - "\n", - "例: (123) 456-7890\n", + "#### **パターン2の詳細構造**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[\"\\(\"] --> B[\"[0-9]{3}\"]\n", + " B -->|\"例: 123\"| C[\"\\)\"]\n", + " C --> D[\"スペース\"]\n", + " D --> E[\"[0-9]{3}\"]\n", + " E -->|\"例: 456\"| F[\"-\"]\n", + " F --> G[\"[0-9]{4}\"]\n", + " G -->|\"例: 7890\"| H[\"結果: (123) 456-7890\"]\n", + " \n", + " style A fill:#ffcdd2\n", + " style B fill:#f8bbd0\n", + " style C fill:#ffcdd2\n", + " style E fill:#e1bee7\n", + " style G fill:#d1c4e9\n", + " style H fill:#c5cae9\n", "```\n", "\n", - "**重要ポイント:** `\\(` と `\\)` はエスケープが必要です(カッコ自体を表すため)\n", - "\n", "---\n", "\n", "### 2. grep コマンドの動作フロー\n", "\n", - "```\n", - "┌─────────────────────────────────────────────────┐\n", - "│ file.txt の内容 │\n", - "├─────────────────────────────────────────────────┤\n", - "│ 行1: 987-123-4567 │\n", - "│ 行2: 123 456 7890 │\n", - "│ 行3: (123) 456-7890 │\n", - "├─────────────────────────────────────────────────┤\n", - " ↓ grep -E で各行をチェック\n", - "├─────────────────────────────────────────────────┤\n", - "│ パターンマッチング │\n", - "├─────────────────────────────────────────────────┤\n", - "│ 行1: 987-123-4567 │\n", - "│ ✓ パターン1にマッチ → 出力 │\n", - "│ │\n", - "│ 行2: 123 456 7890 │\n", - "│ ✗ どちらのパターンにもマッチしない │\n", - "│ (スペースがハイフンではない) │\n", - "│ │\n", - "│ 行3: (123) 456-7890 │\n", - "│ ✓ パターン2にマッチ → 出力 │\n", - "├─────────────────────────────────────────────────┤\n", - " ↓ 結果出力\n", - "├─────────────────────────────────────────────────┤\n", - "│ 987-123-4567 │\n", - "│ (123) 456-7890 │\n", - "└─────────────────────────────────────────────────┘\n", + "```mermaid\n", + "flowchart TD\n", + " A[\"file.txt
行1: 987-123-4567
行2: 123 456 7890
行3: (123) 456-7890\"] --> B[\"grep -E でパターンマッチング開始\"]\n", + " \n", + " B --> C1[\"行1をチェック:
987-123-4567\"]\n", + " B --> C2[\"行2をチェック:
123 456 7890\"]\n", + " B --> C3[\"行3をチェック:
(123) 456-7890\"]\n", + " \n", + " C1 --> D1{\"パターン1
にマッチ?\"}\n", + " D1 -->|\"✓ YES\"| E1[\"出力に追加\"]\n", + " \n", + " C2 --> D2{\"どちらかの
パターンに
マッチ?\"}\n", + " D2 -->|\"✗ NO
(スペース区切り)\"| E2[\"スキップ\"]\n", + " \n", + " C3 --> D3{\"パターン2
にマッチ?\"}\n", + " D3 -->|\"✓ YES\"| E3[\"出力に追加\"]\n", + " \n", + " E1 --> F[\"最終出力\"]\n", + " E2 --> F\n", + " E3 --> F\n", + " \n", + " F --> G[\"987-123-4567
(123) 456-7890\"]\n", + " \n", + " style A fill:#e3f2fd\n", + " style D1 fill:#c8e6c9\n", + " style D2 fill:#ffcdd2\n", + " style D3 fill:#c8e6c9\n", + " style E1 fill:#a5d6a7\n", + " style E2 fill:#ef9a9a\n", + " style E3 fill:#a5d6a7\n", + " style G fill:#81c784\n", "```\n", "\n", "---\n", "\n", - "### 3. オプションの説明\n", - "\n", - "```bash\n", - "grep -E '^pattern$' file.txt\n", - " │ │ └─ 入力ファイル\n", - " │ └─ 正規表現パターン\n", - " └─ Extended Regular Expression (拡張正規表現)\n", + "### 3. オプションと構文要素の説明\n", + "\n", + "```mermaid\n", + "graph TD\n", + " A[\"grep -E '^pattern$' file.txt\"] --> B[\"-E オプション\"]\n", + " A --> C[\"^ アンカー\"]\n", + " A --> D[\"$ アンカー\"]\n", + " A --> E[\"file.txt\"]\n", + " \n", + " B --> B1[\"拡張正規表現を有効化
+, ?, |, () が使用可能\"]\n", + " C --> C1[\"行頭にマッチ
余分な前置文字を排除\"]\n", + " D --> D1[\"行末にマッチ
余分な後置文字を排除\"]\n", + " E --> E1[\"入力ファイル
各行を順次処理\"]\n", + " \n", + " style A fill:#e1f5ff\n", + " style B fill:#fff9c4\n", + " style C fill:#c8e6c9\n", + " style D fill:#c8e6c9\n", + " style E fill:#ffccbc\n", "```\n", "\n", - "- **`-E`**: 拡張正規表現を使用(+, ?, |, () などが使える)\n", - "- **`^`**: 行頭にマッチ(余分な文字がないことを保証)\n", - "- **`$`**: 行末にマッチ(余分な文字がないことを保証)\n", - "\n", "---\n", "\n", - "### 4. テストケースの検証\n", - "\n", - "#### **有効な番号**\n", - "```\n", - "✓ 987-123-4567\n", - " [0-9]{3}-[0-9]{3}-[0-9]{4} にマッチ\n", - " \n", - "✓ (123) 456-7890\n", - " \\([0-9]{3}\\) [0-9]{3}-[0-9]{4} にマッチ\n", - "```\n", - "\n", - "#### **無効な番号**\n", - "```\n", - "✗ 123 456 7890\n", - " 理由: ハイフンではなくスペースで区切られている\n", - " \n", - "✗ 1234567890\n", - " 理由: 区切り文字がない\n", - " \n", - "✗ (123)456-7890\n", - " 理由: カッコの後にスペースがない\n", - " \n", - "✗ 12-345-6789\n", - " 理由: 最初のグループが2桁(3桁が必要)\n", + "### 4. テストケースの検証フロー\n", + "\n", + "```mermaid\n", + "flowchart TD\n", + " A[\"入力電話番号\"] --> B{\"形式チェック\"}\n", + " \n", + " B -->|\"パターン1\"| C1[\"xxx-xxx-xxxx\"]\n", + " B -->|\"パターン2\"| C2[\"(xxx) xxx-xxxx\"]\n", + " B -->|\"その他\"| C3[\"無効な形式\"]\n", + " \n", + " C1 --> D1{\"各部分が
正しい桁数?\"}\n", + " D1 -->|\"✓ YES\"| E1[\"✓ 有効
例: 987-123-4567\"]\n", + " D1 -->|\"✗ NO\"| F1[\"✗ 無効
例: 12-345-6789\"]\n", + " \n", + " C2 --> D2{\"カッコとスペースが
正しい位置?\"}\n", + " D2 -->|\"✓ YES\"| E2[\"✓ 有効
例: (123) 456-7890\"]\n", + " D2 -->|\"✗ NO\"| F2[\"✗ 無効
例: (123)456-7890\"]\n", + " \n", + " C3 --> F3[\"✗ 無効
例: 123 456 7890
例: 1234567890\"]\n", + " \n", + " style E1 fill:#a5d6a7\n", + " style E2 fill:#a5d6a7\n", + " style F1 fill:#ef9a9a\n", + " style F2 fill:#ef9a9a\n", + " style F3 fill:#ef9a9a\n", "```\n", "\n", "---\n", "\n", - "### 5. 実行例\n", - "\n", - "```bash\n", - "# file.txtを作成\n", - "$ cat > file.txt << EOF\n", - "987-123-4567\n", - "123 456 7890\n", - "(123) 456-7890\n", - "EOF\n", - "\n", - "# スクリプトを実行\n", - "$ grep -E '^([0-9]{3}-[0-9]{3}-[0-9]{4}|\\([0-9]{3}\\) [0-9]{3}-[0-9]{4})$' file.txt\n", - "\n", - "# 出力\n", - "987-123-4567\n", - "(123) 456-7890\n", + "### 5. 実行例のシーケンス\n", + "\n", + "```mermaid\n", + "sequenceDiagram\n", + " participant User\n", + " participant Shell\n", + " participant grep\n", + " participant file.txt\n", + " \n", + " User->>Shell: cat > file.txt\n", + " Shell->>file.txt: 987-123-4567
123 456 7890
(123) 456-7890\n", + " \n", + " User->>Shell: grep -E '^pattern$' file.txt\n", + " Shell->>grep: コマンド実行\n", + " \n", + " grep->>file.txt: 行1を読み込み\n", + " file.txt-->>grep: 987-123-4567\n", + " grep->>grep: パターン1にマッチ ✓\n", + " grep->>Shell: 987-123-4567 を出力\n", + " \n", + " grep->>file.txt: 行2を読み込み\n", + " file.txt-->>grep: 123 456 7890\n", + " grep->>grep: マッチせず ✗\n", + " \n", + " grep->>file.txt: 行3を読み込み\n", + " file.txt-->>grep: (123) 456-7890\n", + " grep->>grep: パターン2にマッチ ✓\n", + " grep->>Shell: (123) 456-7890 を出力\n", + " \n", + " Shell->>User: 987-123-4567
(123) 456-7890\n", "```\n", "\n", "---\n", diff --git a/Shell/Bash/Leetcode/194. Transpose File/TransposeFile.ipynb b/Shell/Bash/Leetcode/194. Transpose File/TransposeFile.ipynb index 41b2285e..05aabb99 100644 --- a/Shell/Bash/Leetcode/194. Transpose File/TransposeFile.ipynb +++ b/Shell/Bash/Leetcode/194. Transpose File/TransposeFile.ipynb @@ -386,87 +386,338 @@ "id": "fe45a9c8", "metadata": {}, "source": [ - "## パフォーマンス分析\n", + "## 問題の理解\n", "\n", - "### **時間計算量の比較**\n", + "この問題は**行列の転置**と同じ操作です。元のファイルの行と列を入れ替えます。\n", + "\n", + "### 入力例の視覚化\n", "\n", + "```mermaid\n", + "graph TD\n", + " subgraph \"元のファイル (2列 × 3行)\"\n", + " A[\"行1: name age\"]\n", + " B[\"行2: alice 21\"]\n", + " C[\"行3: ryan 30\"]\n", + " end\n", + " \n", + " subgraph \"列の構造\"\n", + " D[\"列1: name, alice, ryan\"]\n", + " E[\"列2: age, 21, 30\"]\n", + " end\n", + " \n", + " A --> D\n", + " A --> E\n", + " B --> D\n", + " B --> E\n", + " C --> D\n", + " C --> E\n", + " \n", + " style A fill:#e1f5ff\n", + " style B fill:#e1f5ff\n", + " style C fill:#e1f5ff\n", + " style D fill:#fff4e1\n", + " style E fill:#fff4e1\n", "```\n", - "元の解法:\n", - "- 読み込み: O(行数 × 列数)\n", - "- 出力: O(行数 × 列数)\n", - "- 合計: O(2 × 行数 × 列数)\n", "\n", - "改善版:\n", - "- 読み込み+連結: O(行数 × 列数)\n", - "- 出力: O(列数)\n", - "- 合計: O(行数 × 列数 + 列数)\n", + "### 出力の視覚化\n", + "\n", + "```mermaid\n", + "graph LR\n", + " subgraph \"転置後 (3列 × 2行)\"\n", + " A[\"行1: name alice ryan\"]\n", + " B[\"行2: age 21 30\"]\n", + " end\n", + " \n", + " subgraph \"元の列が行に変換\"\n", + " C[\"元の列1 → 行1\"]\n", + " D[\"元の列2 → 行2\"]\n", + " end\n", + " \n", + " C --> A\n", + " D --> B\n", + " \n", + " style A fill:#d1fae5\n", + " style B fill:#d1fae5\n", + " style C fill:#fef3c7\n", + " style D fill:#fef3c7\n", "```\n", "\n", - "### **メモリ使用量の比較**\n", + "## 解法の詳細説明\n", "\n", + "### **ステップ1: データの読み込みと配列への格納**\n", + "\n", + "```bash\n", + "awk '{\n", + " for (i = 1; i <= NF; i++) {\n", + " a[NR, i] = $i\n", + " }\n", + "}'\n", "```\n", - "元の解法:\n", - "┌──────────────────────────────┐\n", - "│ 2次元配列: 行数 × 列数 個の要素 │\n", - "│ 一時文字列: 列数 個 │\n", - "│ 合計: O(行数 × 列数) │\n", - "└──────────────────────────────┘\n", "\n", - "改善版:\n", - "┌──────────────────────────────┐\n", - "│ 1次元配列: 列数 個の文字列 │\n", - "│ 各文字列長: 行数 × 平均単語長 │\n", - "│ 合計: O(列数 × 行数) だが │\n", - "│ 実装が効率的 │\n", - "└──────────────────────────────┘\n", + "**図解:**\n", + "\n", + "```mermaid\n", + "flowchart TD\n", + " subgraph \"読み込み処理\"\n", + " A[\"NR=1: name age\"] --> B[\"a[1,1]=name
a[1,2]=age\"]\n", + " C[\"NR=2: alice 21\"] --> D[\"a[2,1]=alice
a[2,2]=21\"]\n", + " E[\"NR=3: ryan 30\"] --> F[\"a[3,1]=ryan
a[3,2]=30\"]\n", + " end\n", + " \n", + " subgraph \"結果の2次元配列\"\n", + " G[\"行1: [name, age]\"]\n", + " H[\"行2: [alice, 21]\"]\n", + " I[\"行3: [ryan, 30]\"]\n", + " end\n", + " \n", + " B --> G\n", + " D --> H\n", + " F --> I\n", + " \n", + " style A fill:#e1f5ff\n", + " style C fill:#e1f5ff\n", + " style E fill:#e1f5ff\n", + " style B fill:#fff4e1\n", + " style D fill:#fff4e1\n", + " style F fill:#fff4e1\n", + " style G fill:#d1fae5\n", + " style H fill:#d1fae5\n", + " style I fill:#d1fae5\n", "```\n", "\n", - "### **実行例の詳細図解**\n", + "**変数の説明:**\n", + "- `NR`: 現在の行番号 (Number of Records)\n", + "- `NF`: 現在の行のフィールド数 (Number of Fields)\n", + "- `$i`: i番目のフィールド\n", + "- `a[NR, i]`: 2次元配列 (行, 列)\n", + "\n", + "### **ステップ2: 最大列数の記録**\n", "\n", + "```bash\n", + "NF > p { p = NF }\n", "```\n", - "入力: file.txt\n", - "┌─────────────┐\n", - "│ name age │\n", - "│ alice 21 │\n", - "│ ryan 30 │\n", - "└─────────────┘\n", "\n", - "処理フロー:\n", + "各行のフィールド数をチェックし、最大値を`p`に保存します。\n", "\n", - "NR=1: name age\n", - " ↓ ↓\n", - " a[1]=\"name\"\n", - " a[2]=\"age\"\n", + "### **ステップ3: 転置して出力**\n", "\n", - "NR=2: alice 21\n", - " ↓ ↓\n", - " a[1]=\"name alice\" ← スペース追加して連結\n", - " a[2]=\"age 21\"\n", + "```bash\n", + "END {\n", + " for (j = 1; j <= p; j++) {\n", + " str = a[1, j]\n", + " for (i = 2; i <= NR; i++) {\n", + " str = str \" \" a[i, j]\n", + " }\n", + " print str\n", + " }\n", + "}\n", + "```\n", "\n", - "NR=3: ryan 30\n", - " ↓ ↓\n", - " a[1]=\"name alice ryan\"\n", - " a[2]=\"age 21 30\"\n", + "**図解:**\n", "\n", - "END処理:\n", - " print a[1] → \"name alice ryan\"\n", - " print a[2] → \"age 21 30\"\n", + "```mermaid\n", + "flowchart TD\n", + " subgraph \"j=1 (元の列1)\"\n", + " A[\"a[1,1]=name\"] --> B[\"str='name'\"]\n", + " C[\"a[2,1]=alice\"] --> D[\"str='name alice'\"]\n", + " E[\"a[3,1]=ryan\"] --> F[\"str='name alice ryan'\"]\n", + " F --> G[\"出力: 'name alice ryan'\"]\n", + " end\n", + " \n", + " subgraph \"j=2 (元の列2)\"\n", + " H[\"a[1,2]=age\"] --> I[\"str='age'\"]\n", + " J[\"a[2,2]=21\"] --> K[\"str='age 21'\"]\n", + " L[\"a[3,2]=30\"] --> M[\"str='age 21 30'\"]\n", + " M --> N[\"出力: 'age 21 30'\"]\n", + " end\n", + " \n", + " style A fill:#e1f5ff\n", + " style C fill:#e1f5ff\n", + " style E fill:#e1f5ff\n", + " style H fill:#e1f5ff\n", + " style J fill:#e1f5ff\n", + " style L fill:#e1f5ff\n", + " style G fill:#d1fae5\n", + " style N fill:#d1fae5\n", "```\n", "\n", - "## ベンチマーク予想\n", + "## パフォーマンス比較と改善点\n", "\n", - "改善版の期待値:\n", - "- **Runtime**: 40-50ms (約40-50%改善)\n", - "- **Memory**: 4-5MB (約35-40%改善)\n", + "### **元の解法の問題点**\n", + "\n", + "```mermaid\n", + "graph TD\n", + " subgraph \"問題1: 2次元配列の使用\"\n", + " A[\"メモリ使用量: O(行数 × 列数)\"]\n", + " B[\"各セルを個別に保存\"]\n", + " C[\"アクセスオーバーヘッドが大きい\"]\n", + " end\n", + " \n", + " subgraph \"問題2: 二重ループでの文字列連結\"\n", + " D[\"毎回新しい文字列を作成\"]\n", + " E[\"文字列を何度も再構築\"]\n", + " F[\"パフォーマンス低下\"]\n", + " end\n", + " \n", + " A --> D\n", + " B --> E\n", + " C --> F\n", + " \n", + " style A fill:#fee2e2\n", + " style B fill:#fee2e2\n", + " style C fill:#fee2e2\n", + " style D fill:#fef3c7\n", + " style E fill:#fef3c7\n", + " style F fill:#fef3c7\n", + "```\n", + "\n", + "### **改善版の利点**\n", "\n", - "### **主な改善要因**\n", + "```mermaid\n", + "graph LR\n", + " subgraph \"改善1: 1次元配列 + 文字列連結\"\n", + " A[\"メモリ: O(列数)のみ\"]\n", + " B[\"各列を1つの文字列として保存\"]\n", + " C[\"a[1]='name alice ryan'
a[2]='age 21 30'\"]\n", + " end\n", + " \n", + " subgraph \"改善2: 処理中に連結\"\n", + " D[\"読み込みながら連結\"]\n", + " E[\"単純な出力のみ\"]\n", + " F[\"パフォーマンス向上\"]\n", + " end\n", + " \n", + " A --> D\n", + " B --> E\n", + " C --> F\n", + " \n", + " style A fill:#d1fae5\n", + " style B fill:#d1fae5\n", + " style C fill:#d1fae5\n", + " style D fill:#dbeafe\n", + " style E fill:#dbeafe\n", + " style F fill:#dbeafe\n", + "```\n", + "\n", + "## パフォーマンス分析\n", + "\n", + "### **時間計算量の比較**\n", + "\n", + "```mermaid\n", + "graph TD\n", + " subgraph \"元の解法\"\n", + " A[\"読み込み: O(行数 × 列数)\"]\n", + " B[\"出力: O(行数 × 列数)\"]\n", + " C[\"合計: O(2 × 行数 × 列数)\"]\n", + " end\n", + " \n", + " subgraph \"改善版\"\n", + " D[\"読み込み+連結: O(行数 × 列数)\"]\n", + " E[\"出力: O(列数)\"]\n", + " F[\"合計: O(行数 × 列数 + 列数)\"]\n", + " end\n", + " \n", + " A --> B --> C\n", + " D --> E --> F\n", + " \n", + " C -.->|\"遅い\"| G[パフォーマンス比較]\n", + " F -.->|\"速い\"| G\n", + " \n", + " style C fill:#fee2e2\n", + " style F fill:#d1fae5\n", + " style G fill:#e0e7ff\n", + "```\n", + "\n", + "### **メモリ使用量の比較**\n", + "\n", + "```mermaid\n", + "graph TD\n", + " subgraph \"元の解法\"\n", + " A[\"2次元配列:
行数 × 列数 個の要素\"]\n", + " B[\"一時文字列:
列数 個\"]\n", + " C[\"合計: O(行数 × 列数)\"]\n", + " end\n", + " \n", + " subgraph \"改善版\"\n", + " D[\"1次元配列:
列数 個の文字列\"]\n", + " E[\"各文字列長:
行数 × 平均単語長\"]\n", + " F[\"合計: O(列数 × 行数)
だが実装が効率的\"]\n", + " end\n", + " \n", + " A --> B --> C\n", + " D --> E --> F\n", + " \n", + " C -.->|\"メモリ多\"| G[メモリ比較]\n", + " F -.->|\"メモリ少\"| G\n", + " \n", + " style C fill:#fee2e2\n", + " style F fill:#d1fae5\n", + " style G fill:#e0e7ff\n", + "```\n", + "\n", + "### **実行例の詳細図解**\n", + "\n", + "```mermaid\n", + "sequenceDiagram\n", + " participant Input as file.txt\n", + " participant AWK as AWK処理\n", + " participant Array as 配列a[]\n", + " participant Output as 出力\n", + " \n", + " Note over Input: name age
alice 21
ryan 30\n", + " \n", + " Input->>AWK: NR=1: name age\n", + " AWK->>Array: a[1]=\"name\"
a[2]=\"age\"\n", + " \n", + " Input->>AWK: NR=2: alice 21\n", + " AWK->>Array: a[1]=\"name alice\"
a[2]=\"age 21\"\n", + " \n", + " Input->>AWK: NR=3: ryan 30\n", + " AWK->>Array: a[1]=\"name alice ryan\"
a[2]=\"age 21 30\"\n", + " \n", + " Array->>Output: print a[1]\n", + " Note over Output: name alice ryan\n", + " \n", + " Array->>Output: print a[2]\n", + " Note over Output: age 21 30\n", + "```\n", + "\n", + "## ベンチマーク予想\n", + "\n", + "```mermaid\n", + "graph LR\n", + " subgraph \"改善版の期待値\"\n", + " A[\"Runtime: 40-50ms
(約40-50%改善)\"]\n", + " B[\"Memory: 4-5MB
(約35-40%改善)\"]\n", + " end\n", + " \n", + " subgraph \"主な改善要因\"\n", + " C[\"配列アクセスの削減:
2次元→1次元\"]\n", + " D[\"ループネストの削減:
二重ループ→単一ループ\"]\n", + " E[\"文字列操作の最適化:
逐次連結→直接連結\"]\n", + " F[\"条件分岐の最適化:
三項演算子の使用\"]\n", + " end\n", + " \n", + " C --> A\n", + " D --> A\n", + " E --> B\n", + " F --> B\n", + " \n", + " style A fill:#d1fae5\n", + " style B fill:#d1fae5\n", + " style C fill:#dbeafe\n", + " style D fill:#dbeafe\n", + " style E fill:#dbeafe\n", + " style F fill:#dbeafe\n", + "```\n", "\n", - "1. **配列アクセスの削減**: 2次元→1次元\n", - "2. **ループネストの削減**: 二重ループ→単一ループ\n", - "3. **文字列操作の最適化**: 逐次連結→直接連結\n", - "4. **条件分岐の最適化**: 三項演算子の使用\n", + "この最適化により、上位50-70%のパフォーマンスが期待できます!\n", "\n", - "この最適化により、上位50-70%のパフォーマンスが期待できます!" + "主な変更点:\n", + "1. ASCII図をmermaid形式のグラフに変換\n", + "2. フローチャート、シーケンス図、グラフを適切に使用\n", + "3. 色分けでわかりやすく視覚化\n", + "4. 矢印や関係性を明確に表現" ] } ], diff --git a/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb b/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb index be4446d5..55a01a24 100644 --- a/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb +++ b/Shell/Bash/Leetcode/195. Tenth Line/TenthLine.ipynb @@ -108,20 +108,14 @@ "\n", "**動作原理の図解:**\n", "\n", - "```\n", - "file.txt sed -n '10p' 出力\n", - "┌─────────┐ ┌──────────────┐ ┌──────────┐\n", - "│ Line 1 │──┐ │ │ │ │\n", - "│ Line 2 │ │ │ -n: 自動出力 │ │ │\n", - "│ Line 3 │ │ │ OFF │ │ │\n", - "│ Line 4 │ ├──→│ │────────→│ │\n", - "│ Line 5 │ │ │ 10p: 10行目 │ │ │\n", - "│ Line 6 │ │ │ のみ出力 │ │ │\n", - "│ Line 7 │ │ │ │ │ │\n", - "│ Line 8 │ │ │ │ │ │\n", - "│ Line 9 │ │ │ │ │ │\n", - "│ Line 10 │──┘ │ ✓ │ │ Line 10 │\n", - "└─────────┘ └──────────────┘ └──────────┘\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
Line 1-10] --> B[sed -n '10p'
-n: 自動出力OFF
10p: 10行目のみ出力]\n", + " B --> C[出力
Line 10]\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style C fill:#e8f5e9\n", "```\n", "\n", "**特徴:**\n", @@ -131,7 +125,7 @@ "\n", "---\n", "\n", - "### **解法2: `head` と `tail` の組み合わせ**\n", + "### **解法2: `head` と `tail` の組み合わせ(非推奨)**\n", "\n", "```bash\n", "head -n 10 file.txt | tail -n 1\n", @@ -139,22 +133,18 @@ "\n", "**動作原理の図解:**\n", "\n", - "```\n", - "file.txt head -n 10 tail -n 1 出力\n", - "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", - "│ Line 1 │ │ Line 1 │ │ │ │ │\n", - "│ Line 2 │ │ Line 2 │ │ │ │ │\n", - "│ Line 3 │ │ Line 3 │ │ │ │ │\n", - "│ Line 4 │───→│ Line 4 │ │ │ │ │\n", - "│ Line 5 │ │ Line 5 │ │ │ │ │\n", - "│ Line 6 │ │ Line 6 │──────→│ 最後の1行 │───→│ Line 10 │\n", - "│ Line 7 │ │ Line 7 │ │ を取得 │ │ │\n", - "│ Line 8 │ │ Line 8 │ │ │ │ │\n", - "│ Line 9 │ │ Line 9 │ │ ✓ │ │ │\n", - "│ Line 10 │ │ Line 10 │ │ Line 10 │ │ │\n", - "│ Line 11 │ └──────────┘ └──────────┘ └──────────┘\n", - "│ Line 12 │ ↑ ここまで\n", - "└─────────┘\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
Line 1-12] --> B[head -n 10
最初の10行を取得]\n", + " B --> C[Line 1-10]\n", + " C --> D[tail -n 1
最後の1行を取得]\n", + " D --> E[出力
Line 10]\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style C fill:#f3e5f5\n", + " style D fill:#fff3e0\n", + " style E fill:#e8f5e9\n", "```\n", "\n", "**処理の流れ:**\n", @@ -164,6 +154,7 @@ "**特徴:**\n", "- 直感的で理解しやすい\n", "- 2つのコマンドを組み合わせるため、やや冗長\n", + "- ⚠️ **10行未満のファイルでは誤動作する**\n", "\n", "---\n", "\n", @@ -175,20 +166,21 @@ "\n", "**動作原理の図解:**\n", "\n", - "```\n", - "file.txt awk 'NR==10' 出力\n", - "┌─────────┐ ┌───────────────────┐ ┌──────────┐\n", - "│ Line 1 │ NR=1 ──→ │ NR==10? No │ │ │\n", - "│ Line 2 │ NR=2 ──→ │ NR==10? No │ │ │\n", - "│ Line 3 │ NR=3 ──→ │ NR==10? No │ │ │\n", - "│ Line 4 │ NR=4 ──→ │ NR==10? No │ │ │\n", - "│ Line 5 │ NR=5 ──→ │ NR==10? No │────────→│ │\n", - "│ Line 6 │ NR=6 ──→ │ NR==10? No │ │ │\n", - "│ Line 7 │ NR=7 ──→ │ NR==10? No │ │ │\n", - "│ Line 8 │ NR=8 ──→ │ NR==10? No │ │ │\n", - "│ Line 9 │ NR=9 ──→ │ NR==10? No │ │ │\n", - "│ Line 10 │ NR=10 ──→ │ NR==10? Yes! ✓ │ │ Line 10 │\n", - "└─────────┘ └───────────────────┘ └──────────┘\n", + "```mermaid\n", + "graph TD\n", + " A[file.txt] --> B{awk 'NR==10'}\n", + " B -->|NR=1| C[Line 1: No]\n", + " B -->|NR=2| D[Line 2: No]\n", + " B -->|NR=3| E[Line 3: No]\n", + " B -->|...| F[...]\n", + " B -->|NR=9| G[Line 9: No]\n", + " B -->|NR=10| H[Line 10: Yes ✓]\n", + " H --> I[出力
Line 10]\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style H fill:#c8e6c9\n", + " style I fill:#e8f5e9\n", "```\n", "\n", "**特徴:**\n", @@ -200,22 +192,22 @@ "\n", "## **10行未満のファイルへの対応**\n", "\n", - "ファイルが10行未満の場合の動作比較:\n", - "\n", - "```\n", - "5行のファイルの場合:\n", - "\n", - "┌─────────┐\n", - "│ Line 1 │\n", - "│ Line 2 │\n", - "│ Line 3 │ ← 5行しかない\n", - "│ Line 4 │\n", - "│ Line 5 │\n", - "└─────────┘\n", - "\n", - "sed -n '10p' → 何も出力しない\n", - "head -n 10 | tail -n 1 → Line 5 を出力\n", - "awk 'NR==10' → 何も出力しない\n", + "### **5行のファイルの場合の動作比較:**\n", + "\n", + "```mermaid\n", + "graph TD\n", + " A[file.txt
5行のみ] --> B[sed -n '10p']\n", + " A --> C[\"head -n 10 | tail -n 1\"]\n", + " A --> D[awk 'NR==10']\n", + " \n", + " B --> E[何も出力しない ✓]\n", + " C --> F[Line 5 を出力 ✗]\n", + " D --> G[何も出力しない ✓]\n", + " \n", + " style A fill:#ffebee\n", + " style E fill:#e8f5e9\n", + " style F fill:#ffcdd2\n", + " style G fill:#e8f5e9\n", "```\n", "\n", "**エラーチェック付きバージョン:**\n", @@ -235,9 +227,9 @@ "\n", "| 解法 | コマンド | 長所 | 短所 | 10行未満の動作 |\n", "|------|----------|------|------|----------------|\n", - "| **sed** | `sed -n '10p'` | シンプル、高速 | sed文法の知識が必要 | 何も出力しない |\n", - "| **head+tail** | `head -n 10 \\| tail -n 1` | 直感的 | 2つのプロセスが必要 | 最終行を出力 |\n", - "| **awk** | `awk 'NR==10'` | 柔軟性が高い | やや複雑 | 何も出力しない |\n", + "| **sed** | `sed -n '10p'` | シンプル、高速 | sed文法の知識が必要 | 何も出力しない ✓ |\n", + "| **head+tail** | `head -n 10 \\| tail -n 1` | 直感的 | 2つのプロセスが必要 | 最終行を出力 ✗ |\n", + "| **awk** | `awk 'NR==10'` | 柔軟性が高い | やや複雑 | 何も出力しない ✓ |\n", "\n", "---\n", "\n", @@ -247,136 +239,134 @@ "\n", "これが最も一般的で、多くの場合に推奨される方法です!\n", "\n", - "ご指摘ありがとうございます!問題を特定しました。\n", - "\n", - "## **問題点の分析**\n", - "\n", - "ファイルが**9行しかない**場合、`head -n 10`は9行すべてを出力し、`tail -n 1`はその最後の行(9行目)を出力してしまいます。\n", - "\n", - "### **動作の図解**\n", - "\n", - "```\n", - "file.txt (9行) head -n 10 tail -n 1 出力\n", - "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", - "│ 1 │ │ 1 │ │ │ │ │\n", - "│ 2 │ │ 2 │ │ │ │ │\n", - "│ 3 │ │ 3 │ │ │ │ │\n", - "│ 4 │──────→ 4 │ │ │ │ │\n", - "│ 5 │ │ 5 │──────→ 最後の1行 │────→ 9 │\n", - "│ 6 │ │ 6 │ │ を取得 │ │ │\n", - "│ 7 │ │ 7 │ │ ✓ │ │ ✗ 誤り! │\n", - "│ 8 │ │ 8 │ │ 9 │ │ │\n", - "│ 9 │ │ 9 │ │ │ │ │\n", - "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", - " 9行しかない → 9行出力 → 9を出力\n", - "\n", - "期待される出力: 何も出力しない(10行目は存在しない)\n", - "実際の出力: 9\n", - "```\n", - "\n", - "## **正しい解法**## **3つの正しい解法**\n", - "\n", - "### **解法1: `sed -n '10p'`(最も推奨)**\n", - "\n", - "```bash\n", - "sed -n '10p' file.txt\n", - "```\n", - "\n", - "✅ **正しい動作:**\n", - "- 10行ある場合: 10行目を出力\n", - "- 9行しかない場合: 何も出力しない\n", - "\n", "---\n", "\n", - "### **解法2: `awk 'NR==10'`(推奨)**\n", - "\n", - "```bash\n", - "awk 'NR==10' file.txt\n", + "## **問題点の分析: なぜ `head -n 10 | tail -n 1` は間違いなのか**\n", + "\n", + "### **9行のファイルでの動作:**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
9行のみ] --> B[head -n 10
9行すべてを出力]\n", + " B --> C[Line 1-9]\n", + " C --> D[tail -n 1
最後の1行]\n", + " D --> E[出力: Line 9 ✗
期待: 何も出力しない]\n", + " \n", + " style A fill:#ffebee\n", + " style B fill:#fff3e0\n", + " style C fill:#f3e5f5\n", + " style D fill:#fff3e0\n", + " style E fill:#ffcdd2\n", "```\n", "\n", - "✅ **正しい動作:**\n", - "- 10行ある場合: 10行目を出力\n", - "- 9行しかない場合: 何も出力しない\n", + "### **10行のファイルでの動作:**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
10行] --> B[head -n 10
10行を出力]\n", + " B --> C[Line 1-10]\n", + " C --> D[tail -n 1
最後の1行]\n", + " D --> E[出力: Line 10 ✓]\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style C fill:#f3e5f5\n", + " style D fill:#fff3e0\n", + " style E fill:#e8f5e9\n", + "```\n", "\n", "---\n", "\n", - "### **解法3: `tail -n +10 | head -n 1`(推奨)**\n", + "## **正しい解法: `tail -n +10 | head -n 1`**\n", "\n", "```bash\n", "tail -n +10 file.txt | head -n 1\n", "```\n", "\n", - "**動作原理の図解:**\n", - "\n", + "### **9行のファイルでの動作:**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
9行のみ] --> B[tail -n +10
10行目から取得]\n", + " B --> C[空
10行目が存在しない]\n", + " C --> D[head -n 1]\n", + " D --> E[出力: 空 ✓]\n", + " \n", + " style A fill:#ffebee\n", + " style B fill:#fff3e0\n", + " style C fill:#f5f5f5\n", + " style D fill:#fff3e0\n", + " style E fill:#e8f5e9\n", "```\n", - "file.txt (9行) tail -n +10 head -n 1 出力\n", - "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", - "│ 1 │ │ │ │ │ │ │\n", - "│ 2 │ │ │ │ │ │ │\n", - "│ 3 │ │ │ │ │ │ │\n", - "│ 4 │ │ │ │ │ │ │\n", - "│ 5 │ │ (空) │──────→ (空) │────→ (空) │\n", - "│ 6 │ │ │ │ │ │ │\n", - "│ 7 │ │ │ │ │ │ │\n", - "│ 8 │ │ │ │ │ │ │\n", - "│ 9 │ │ │ │ │ │ │\n", - "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", - " 10行目以降がない → 何も出力しない ✓\n", - "\n", - "\n", - "file.txt (10行) tail -n +10 head -n 1 出力\n", - "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", - "│ 1 │ │ │ │ │ │ │\n", - "│ 2 │ │ │ │ │ │ │\n", - "│ 3 │ │ │ │ ✓ │ │ │\n", - "│ 4 │ │ 10 │──────→ 10 │────→ 10 │\n", - "│ 5 │ │ │ │ │ │ │\n", - "│ 6 │ │ │ │ │ │ │\n", - "│ 7 │ │ │ │ │ │ │\n", - "│ 8 │ │ │ │ │ │ │\n", - "│ 9 │ │ │ │ │ │ │\n", - "│ 10 │ │ │ │ │ │ │\n", - "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", + "\n", + "### **10行のファイルでの動作:**\n", + "\n", + "```mermaid\n", + "graph LR\n", + " A[file.txt
10行] --> B[tail -n +10
10行目から取得]\n", + " B --> C[Line 10]\n", + " C --> D[head -n 1
最初の1行]\n", + " D --> E[出力: Line 10 ✓]\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style C fill:#f3e5f5\n", + " style D fill:#fff3e0\n", + " style E fill:#e8f5e9\n", "```\n", "\n", - "✅ **正しい動作:**\n", + "**✅ 正しい動作:**\n", "- `tail -n +10`: 10行目から最後まで取得(+10は「10行目から」の意味)\n", "- `head -n 1`: その最初の1行を取得\n", "\n", "---\n", "\n", - "## **なぜ `head -n 10 | tail -n 1` は間違いなのか**\n", - "\n", - "```\n", - "❌ 間違った解法の動作:\n", - "\n", - "9行のファイル:\n", - "1. head -n 10 → 9行すべてを出力(10行ないので9行しか取れない)\n", - "2. tail -n 1 → その最後の1行 = 9行目を出力\n", - "結果: 9 が出力される(誤り)\n", - "\n", - "10行のファイル:\n", - "1. head -n 10 → 10行を出力\n", - "2. tail -n 1 → その最後の1行 = 10行目を出力\n", - "結果: 10 が出力される(正しい)\n", - "```\n", - "\n", "## **正解のまとめ**\n", "\n", "LeetCode/オンラインジャッジで正解する解法:\n", "\n", "```bash\n", - "# 解法1(最もシンプル)\n", + "# 解法1(最もシンプル)- 推奨 ⭐\n", "sed -n '10p' file.txt\n", "\n", - "# 解法2(汎用性が高い)\n", + "# 解法2(汎用性が高い)- 推奨 ⭐\n", "awk 'NR==10' file.txt\n", "\n", - "# 解法3(tailの+記法を使用)\n", + "# 解法3(tailの+記法を使用)- 推奨 ⭐\n", "tail -n +10 file.txt | head -n 1\n", "```\n", "\n", - "これらはすべて、ファイルが10行未満の場合は**何も出力しない**ため、テストケースをすべてパスします!" + "### **3つの解法の比較フローチャート:**\n", + "\n", + "```mermaid\n", + "graph TD\n", + " A[ファイルの行数] --> B{10行以上?}\n", + " B -->|Yes| C[sed -n '10p']\n", + " B -->|Yes| D[awk 'NR==10']\n", + " B -->|Yes| E[\"tail -n +10 | head -n 1\"]\n", + " B -->|No| F[何も出力しない]\n", + " \n", + " C --> G[Line 10 出力 ✓]\n", + " D --> G\n", + " E --> G\n", + " \n", + " style A fill:#e3f2fd\n", + " style B fill:#fff3e0\n", + " style C fill:#c8e6c9\n", + " style D fill:#c8e6c9\n", + " style E fill:#c8e6c9\n", + " style F fill:#ffcdd2\n", + " style G fill:#e8f5e9\n", + "```\n", + "\n", + "これらはすべて、ファイルが10行未満の場合は**何も出力しない**ため、テストケースをすべてパスします!\n", + "\n", + "主な変更点:\n", + "1. すべてのASCII図をMermaid形式に変換\n", + "2. グラフの種類を適切に選択(`graph LR`、`graph TD`)\n", + "3. スタイリングを追加して視覚的に分かりやすく\n", + "4. 正しい解法と誤った解法を色分けで明示\n", + "5. フローチャートで処理の流れを明確化" ] } ],