Skip to content

Commit 024c651

Browse files
authored
Merge pull request #90 from myoshi2891/dev/macbook_pro
Dev/macbook pro
2 parents 18b960a + 783c538 commit 024c651

14 files changed

Lines changed: 2103 additions & 0 deletions

File tree

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# 以下に、制約 `1 ≤ N < 10^15` を満たす効率的な Python 実装を提示します。
2+
# 逐次的に `f(1) + f(2) + ... + f(N)` を計算すると間に合わないため、**桁DP(Digit DP)** を用いて計算量とメモリ使用量を抑えます。
3+
4+
# ---
5+
6+
# ## ✅ 方針概要
7+
8+
# * 各 `x` に対して `f(x)` は「x の各桁の和」です。
9+
# * `1 〜 N` すべてに対して f(x) を逐次計算するのではなく、**各桁における数字の出現回数**を数えて桁和の合計を導出します。
10+
# * Python の再帰上限とメモリ制限に配慮してメモ化再帰(lru\_cache)を使用。
11+
12+
# ---
13+
14+
# ## ✅ Pythonコード(CPython 3.11.4対応、型・コメント付き)
15+
16+
# ```python
17+
import sys
18+
sys.setrecursionlimit(1 << 25) # 深い再帰に対応
19+
20+
from functools import lru_cache
21+
22+
def main() -> None:
23+
N = input().strip()
24+
print(sum_of_digit_sums(N))
25+
26+
# ------------------- 解法本体 -------------------
27+
28+
def sum_of_digit_sums(N_str: str) -> int:
29+
"""
30+
f(1) + f(2) + ... + f(N) を返す。
31+
32+
:param N_str: N(最大15桁)の文字列
33+
:return: 総和(整数)
34+
"""
35+
digits: list[int] = list(map(int, N_str))
36+
L: int = len(digits)
37+
38+
@lru_cache(maxsize=None)
39+
def dp(pos: int, tight: bool, leading_zero: bool, total: int) -> int:
40+
"""
41+
桁DP:現在の位置から作れる数の各桁の和の合計
42+
43+
:param pos: 現在の桁(0-indexed)
44+
:param tight: 上限制約(Trueならdigits[pos]以下)
45+
:param leading_zero: 現在先頭が0の状態か(0はf(0)=0として無視)
46+
:param total: ここまでの桁和
47+
:return: 桁和の合計
48+
"""
49+
if pos == L:
50+
return 0 if leading_zero else total # leading zero は数と見なさない
51+
52+
limit: int = digits[pos] if tight else 9
53+
res: int = 0
54+
for d in range(0, limit + 1):
55+
next_tight = tight and (d == limit)
56+
next_leading_zero = leading_zero and (d == 0)
57+
add = 0 if next_leading_zero else d
58+
res += dp(pos + 1, next_tight, next_leading_zero, total + add)
59+
return res
60+
61+
return dp(0, True, True, 0)
62+
63+
# ------------------- 入力処理 -------------------
64+
65+
if __name__ == "__main__":
66+
main()
67+
# ```
68+
69+
# ---
70+
71+
# ## ✅ 処理時間・メモリ使用量
72+
73+
# | 指標 | 説明 |
74+
# | --------- | ---------------------------------- |
75+
# | 実行時間 | 約 0.02〜0.1 秒(N < 10¹⁵ まで) |
76+
# | 再帰回数 | 最大で `15 * 2 * 2 * 135` ≒ 数千回(許容範囲) |
77+
# | メモリ使用量 | 数MB以下(再帰キャッシュ + 関数スタック) |
78+
# | Python互換性 | CPython 3.11.4 / PyPy 3対応可 |
79+
80+
# ---
81+
82+
# ## ✅ テスト例
83+
84+
# ### 入力:
85+
86+
# ```
87+
# 4
88+
# ```
89+
90+
# 出力:
91+
92+
# ```
93+
# 10
94+
# ```
95+
96+
# ### 入力:
97+
98+
# ```
99+
# 288
100+
# ```
101+
102+
# 出力:
103+
104+
# ```
105+
# 2826
106+
# ```
107+
108+
# ### 入力(最大級):
109+
110+
# ```
111+
# 999999999999999
112+
# ```
113+
114+
# 出力:
115+
116+
# ```
117+
# ?(すぐに完了、1秒以内)※必要なら値出力可能
118+
# ```
119+
120+
# ---
121+
122+
# ## ✅ 補足:簡易チェック用の愚直解(小N向け)
123+
124+
# ```python
125+
# def brute(N: int) -> int:
126+
# return sum(sum(int(c) for c in str(i)) for i in range(1, N + 1))
127+
# ```
128+
129+
# ---
130+
131+
# ## ✅ まとめ
132+
133+
# * 再帰+メモ化(桁DP)で効率よく解ける。
134+
# * `leading_zero` を管理し、`f(0)` を含めないよう注意。
135+
# * lru\_cache により再帰も高速・省メモリ。
136+
137+
# ---
138+
139+
# 必要であれば「愚直版と差分チェック」「単体テスト」「解の復元」なども提供できます。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// 以下に、`Node.js 18.16.1` 環境で実行可能な `.indexOf()` を使ったシンプルで効率的な **JavaScript(関数形式)** の実装を提示します。
2+
3+
// ---
4+
5+
// ## ✅ 実装コード(JavaScript + `.indexOf()`)
6+
7+
// ```javascript
8+
/**
9+
* haystack から needle を検索し、最初に一致するインデックスを返す。
10+
* 一致しない場合は -1 を返す。
11+
*
12+
* @param {string} haystack - 探索対象の文字列(最大長 10^4)
13+
* @param {string} needle - 探索する部分文字列(最大長 10^4)
14+
* @returns {number} - needle の最初の出現インデックス、存在しない場合は -1
15+
*
16+
* 時間計算量: O(n * m) 最悪ケース(平均は高速)
17+
* 空間計算量: O(1)
18+
*/
19+
function strStrJs(haystack, needle) {
20+
return haystack.indexOf(needle);
21+
}
22+
23+
// ## 🧪 テストケース例
24+
25+
// ```javascript
26+
// console.log(strStr("sadbutsad", "sad")); // => 0
27+
// console.log(strStr("leetcode", "leeto")); // => -1
28+
// console.log(strStr("mississippi", "issip")); // => 4
29+
// console.log(strStr("abc", "")); // => 0
30+
// ```
31+
32+
// ---
33+
34+
// ## 🔍 計算量と実行パフォーマンスの評価
35+
36+
// | 項目 | 値 |
37+
// | --------------- | --------------------------- |
38+
// | **時間計算量** | 平均: `O(n)` / 最悪: `O(n * m)` |
39+
// | **空間計算量** | `O(1)`(追加メモリなし) |
40+
// | **Node.js実行環境** | `v18.16.1`(ECMAScript仕様準拠) |
41+
// | **最大長制約** | 10⁴(LeetCode制限内) |
42+
43+
// ---
44+
45+
// ## ✅ なぜ `.indexOf()` は便利か?
46+
47+
// * ネイティブ実装のため **高速な最適化** がされている。
48+
// * 内部的には **Boyer-Moore** や **KMPのような手法**を使うこともあり、**平均ケースで高速**。
49+
// * コードが非常に短く保守性が高い。
50+
51+
// ---
52+
53+
// ## ❗️注意点
54+
55+
// * 最悪ケースでは `O(n * m)` の比較が発生することがあります。
56+
57+
// * 例: `haystack = "aaaaaaaab"`, `needle = "aaaab"` のようなケース
58+
59+
// ---
60+
61+
// ご希望があれば、`.indexOf()` の内部アルゴリズムや他手法(Rabin-Karp など)との速度比較も可能です。どうぞお申し付けください。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# 以下に、`Python (CPython 3.11.4)` を使用し、LeetCode形式に準拠した `class Solution` を用いた解答を示します。
2+
3+
# 処理効率を重視し、**KMP(Knuth-Morris-Pratt)法**を用いて、`needle`の最初の出現インデックスを効率よく探索します。
4+
5+
# ---
6+
7+
# ### ✅ 実装コード(KMP法使用)
8+
9+
# ```python
10+
from typing import List
11+
12+
class Solution:
13+
def strStr(self, haystack: str, needle: str) -> int:
14+
"""
15+
haystack: str - 探索対象の文字列
16+
needle: str - 探索する部分文字列
17+
return: int - needleの最初の出現インデックス。見つからない場合は-1。
18+
時間計算量: O(n + m), 空間計算量: O(m)
19+
(n = haystackの長さ, m = needleの長さ)
20+
"""
21+
22+
def build_lps(pattern: str) -> List[int]:
23+
"""部分一致テーブル(LPS配列)を構築する"""
24+
lps = [0] * len(pattern)
25+
length = 0 # 前のLPS長
26+
i = 1
27+
28+
while i < len(pattern):
29+
if pattern[i] == pattern[length]:
30+
length += 1
31+
lps[i] = length
32+
i += 1
33+
else:
34+
if length != 0:
35+
length = lps[length - 1]
36+
else:
37+
lps[i] = 0
38+
i += 1
39+
return lps
40+
41+
if not needle:
42+
return 0 # 空文字列は0番目に見つかったとみなす
43+
44+
lps = build_lps(needle)
45+
i = j = 0 # iはhaystack用, jはneedle用
46+
47+
while i < len(haystack):
48+
if haystack[i] == needle[j]:
49+
i += 1
50+
j += 1
51+
if j == len(needle):
52+
return i - j
53+
else:
54+
if j != 0:
55+
j = lps[j - 1]
56+
else:
57+
i += 1
58+
59+
return -1 # 見つからなかった場合
60+
# ```
61+
62+
# ---
63+
64+
# ### 🔍 処理時間・メモリ計算量
65+
66+
# | 項目 | 計算量 |
67+
# | ------ | ------------------------------ |
68+
# | 時間計算量 | `O(n + m)` |
69+
# | 空間計算量 | `O(m)`(LPS配列の構築) |
70+
# | 最悪ケース例 | haystack: 10⁴文字, needle: 10⁴文字 |
71+
72+
# ---
73+
74+
# ### ✅ テストケース例
75+
76+
# ```python
77+
# s = Solution()
78+
# print(s.strStr("sadbutsad", "sad")) # 出力: 0
79+
# print(s.strStr("leetcode", "leeto")) # 出力: -1
80+
# print(s.strStr("aaaabaaabaaac", "abaaac"))# 出力: 7
81+
# print(s.strStr("abc", "")) # 出力: 0(空needle)
82+
# ```
83+
84+
# ---
85+
86+
# ### ✅ 備考
87+
88+
# * Pythonの組み込み関数 `haystack.find(needle)` を使えばワンライナーでも同様の結果が得られますが、KMP法はアルゴリズム的理解と応用力の訓練に適しています。
89+
# * LPS(Longest Prefix which is also Suffix)は、部分一致の再利用を可能にし、不要な比較を省きます。
90+
91+
# ---
92+
93+
# 必要があれば **図解付き**でLPS配列やKMPステップの説明も可能です。希望があればお知らせください。

0 commit comments

Comments
 (0)