|
| 1 | +# 1015. Smallest Integer Divisible by K |
| 2 | + |
| 3 | +Given a positive integer `k`, you need to find the length of the smallest positive integer `n` such that n is divisible by `k`, and `n` only contains the digit `1`. |
| 4 | + |
| 5 | +Return the length of `n`. |
| 6 | +If there is no such `n`, return -1. |
| 7 | + |
| 8 | +Note: `n` may not fit in a 64-bit signed integer. |
| 9 | + |
| 10 | +**Constraints:** |
| 11 | + |
| 12 | +- `1 <= k <= 10^5` |
| 13 | + |
| 14 | +## 基礎思路 |
| 15 | + |
| 16 | +本題要找的是一個**只由數字 `1` 組成的正整數 `n`**(例如:`1`, `11`, `111`, ...), |
| 17 | +使得它能被給定的正整數 `k` 整除,並回傳這個 `n` 的「位數長度」。若不存在則回傳 `-1`。 |
| 18 | + |
| 19 | +關鍵觀察有以下幾點: |
| 20 | + |
| 21 | +1. **數字結構固定為「重複的 1」** |
| 22 | + 第 1 個候選是 `1`,第 2 個是 `11`,第 3 個是 `111`,依此類推。 |
| 23 | + 這些數字可以用遞推的方式建立: |
| 24 | + |
| 25 | + * $R_1 = 1$ |
| 26 | + * $R_2 = 11 = (R_1 * 10 + 1)$ |
| 27 | + * $R_3 = 111 = (R_2 * 10 + 1)$ |
| 28 | + |
| 29 | + 因為數值本身可能非常巨大,實作上只能追蹤「除以 k 的餘數」。 |
| 30 | + |
| 31 | +2. **避免整數溢位:只追蹤餘數即可** |
| 32 | + 若我們令 $r_i$ 為 $R_i \bmod k$,則有以下遞推關係: |
| 33 | + |
| 34 | + $$ |
| 35 | + r_i = (r_{i-1} \times 10 + 1) \bmod k |
| 36 | + $$ |
| 37 | + |
| 38 | + 只要有某個 $i$ 使得 $r_i = 0$,就代表長度為 $i$ 的全 1 數字可以被 $k$ 整除。 |
| 39 | + |
| 40 | +3. **模運算中的鴿籠原理(餘數有限)** |
| 41 | + 所有可能的餘數只有 $0, 1, ..., k-1$ 共 $k$ 種。 |
| 42 | + 若我們從長度 1 一路試到長度 $k$ 都沒有看到餘數 `0`, |
| 43 | + 則必然已經出現「某個餘數重複」,之後只會在循環中打轉,不可能再得到新的餘數 `0`。 |
| 44 | + 因此 **最長只需要檢查長度 1 到 $k$** ,超過就可以直接判定為不存在。 |
| 45 | + |
| 46 | +4. **2 與 5 的整除性特例** |
| 47 | + 任何只由 `1` 組成的數字,其尾數必為 `1`, |
| 48 | + 因此不可能是 2 的倍數,也不可能是 5 的倍數。 |
| 49 | + 若 $k$ 含有質因數 2 或 5(亦即 $k \bmod 2 == 0$ 或 $k \bmod 5 == 0$), |
| 50 | + 則不可能存在這樣的 $n$,直接回傳 `-1`。 |
| 51 | + |
| 52 | +綜合以上,我們可以以模擬「逐步建立全 1 數字的餘數」的方式來找解, |
| 53 | +並利用 2/5 的特例與鴿籠原理控制迴圈上限。 |
| 54 | + |
| 55 | +## 解題步驟 |
| 56 | + |
| 57 | +### Step 1:特例判斷(必要條件) |
| 58 | + |
| 59 | +若 `k` 可被 2 或 5 整除,repunit 不可能被其整除,因此立即返回 -1。 |
| 60 | + |
| 61 | +```typescript |
| 62 | +// 若能被 2 或 5 整除,repunit 不可能被 k 整除 |
| 63 | +if (k % 2 === 0 || k % 5 === 0) { |
| 64 | + return -1; |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +### Step 2:初始化餘數並開始逐步構造 repunit |
| 69 | + |
| 70 | +使用 `remainder` 表示當前 repunit 的餘數。 |
| 71 | +之後從長度 1 一路嘗試到 k,套用公式更新餘數。 |
| 72 | + |
| 73 | +```typescript |
| 74 | +// 構造 repunit 時的餘數 |
| 75 | +let remainder = 0; |
| 76 | +``` |
| 77 | + |
| 78 | +### Step 3:在迴圈中構造 repunit 的餘數並檢查是否可整除 |
| 79 | + |
| 80 | +每次迴圈將 repunit 往右擴增一位 `1`,並檢查餘數是否變為 0。 |
| 81 | +若是,立刻回傳長度;若直到長度 k 都無法整除,返回 -1。 |
| 82 | + |
| 83 | +```typescript |
| 84 | +// 依鴿籠原理,最多只需搜尋到長度 k |
| 85 | +for (let length = 1; length <= k; length++) { |
| 86 | + // 更新 remainder = (remainder * 10 + 1) % k |
| 87 | + remainder = remainder * 10 + 1; |
| 88 | + remainder = remainder % k; |
| 89 | + |
| 90 | + // 若餘數為 0,表示長度 length 即為答案 |
| 91 | + if (remainder === 0) { |
| 92 | + return length; |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +return -1; |
| 97 | +``` |
| 98 | + |
| 99 | +## 時間複雜度 |
| 100 | + |
| 101 | +- 最多嘗試長度從 1 到 `k` 的所有候選長度,每次更新與檢查為常數時間; |
| 102 | +- 迴圈內僅進行簡單整數運算與取模操作。 |
| 103 | +- 總時間複雜度為 $O(k)$。 |
| 104 | + |
| 105 | +> $O(k)$ |
| 106 | +
|
| 107 | +## 空間複雜度 |
| 108 | + |
| 109 | +- 僅使用常數個變數(`remainder` 與計數變數 `length`); |
| 110 | +- 無額外與 `k` 成比例的資料結構。 |
| 111 | +- 總空間複雜度為 $O(1)$。 |
| 112 | + |
| 113 | +> $O(1)$ |
0 commit comments