---
# System prepended metadata

title: Misty Panther
tags: [Easy]

---

# Misty Panther

###### tags: `Easy`
> [name=出題者 : 曾致銘]

## Problem
給定一個整數 `n` ，要求印出一個長度為 `n` 的字串陣列 `answer` 滿足：
- 若 `i` 可被 `3` 和 `5` 整除，則 `answer[i-1] == "MistyPanther"`。
- 若 `i` 可被 `3` 整除，則 `answer[i-1] == "Misty"`。
- 若 `i` 可被 `5` 整除，則 `answer[i-1] == "Panther"`。
- 若不滿足以上條件，則 `answer[i-1] == i`。

### Examples
範例 1：
```
輸入: 3
輸出: 1 2 Misty
```

範例 2：
```
輸入: 5
輸出: 1 2 Misty 4 Panther
```

範例 3：
```
輸入: 15
輸出: 1 2 Misty 4 Panther Misty 7 8 Misty Panther 11 Misty 13 14 MistyPanther
```

### Constraints
- $1 \le$ `n` $\le 10^4$

### Hints

### Extra Challenge
請思考倘若今天題目給定的字串數量非常多時 (如 I Love Misty Panther 8725)，你的演算法是否會看起來很繁雜？有沒有更簡潔的作法？

## Solution
本篇在討論的是如何精進你的演算法，使得在資料增加時，能夠看起來更加簡潔與易讀。

:::spoiler Description
### Approach 1: 直觀算法

#### 思路
你的直覺想法應該是，判斷 `i` 值能否能被 `3`, `5`, 或同時整除。

#### 算法
1. 創建一個長度為 `n` 的字串陣列 `answer` 作為輸出。
2. 遍歷整數 `i` 滿足 $1 \le$ `i` $\le n$。
3. 判斷如果 `i` 可同時被 `3`, `5` 整除，設 `answer[i-1] = "MistyPanther"`。
4. 否則判斷如果 `i` 可被 `3` 整除，設 `answer[i-1] = "Misty"`。
5. 否則判斷如果 `i` 可被 `5` 整除，設 `answer[i-1] = "Panther"`。
6. 否則，設 `answer[i-1] = i`
7. 重複步驟 3 到步驟 6 直至遍歷完成。

```cpp=
// 以下省略 cin, cout，僅呈現演算法部分
// 步驟 1
string answer[n];

for (int i = 1; i <= n; i++) {
    bool divisibleBy3 = (i % 3 == 0);
    bool divisibleBy5 = (i % 5 == 0);
    
    if (divisibleBy3 && divisibleBy5) {
        // 步驟 3
        answer[i - 1] = "MistyPanther";
    } else if (divisibleBy3) {
        // 步驟 4
        answer[i - 1] = "Misty";
    } else if (divisibleBy5) {
        // 步驟 5
        answer[i - 1] = "Panther";
    } else {
        // 步驟 6
        answer[i - 1] = to_string(i);
    }
}
```

#### 複雜度分析
- 時間複雜度: $O(n)$。
- 空間複雜度: $O(1)$。

### Approach 2: 字串組合

#### 思路
這個算法其實不會減少數學上的計算複雜度，但若資料增加時它能夠更加簡潔。舉例而言，若今天的題目是「Misty Panther 8725」：

```
3 ---> "Misty", 5 ---> "Panther", 7 ---> "8725"
```

若你嘗試使用上一種演算法來處理，那麼你將會有很多條件要檢查：

1. 被 3 整除
2. 被 5 整除
3. 被 7 整除
4. 被 3, 5 整除
5. 被 3, 7 整除
6. 被 5, 7 整除
7. 被 3, 5, 7 整除
8. 不被整除

如此一來，當你的資料增加時，你的條件將會指數型成長。因此，與其把每一個數字的組合都判斷一次，不如只判斷個別數字的整除性，並把符合的字串「加」上題目要求的字串，如以 `i == 15` 來舉例：

```
條件 1: 15 % 3 == 0, answer[14] = "Misty"
條件 2: 15 % 5 == 0, answer[14] += "Panther"
=> answer[14] == "MistyPanther"
```

在這種情況下，我們只需要判斷 2 次而非上一演算法的 3 次，而對於「Misty Panther 8725」來說，我們也只需要判斷 3 次而非 7 次。

#### 算法
```cpp=
// 以下省略 cin, cout，僅呈現演算法部分
// 作為答案的陣列 「answer」
string answer[n];

for (int i = 1; i <= n; i++) {
    bool divisibleBy3 = (i % 3 == 0);
    bool divisibleBy5 = (i % 5 == 0);
    string numAnsStr = "";
    
    if (divisibleBy3) {
        // 可被 3 整除，字串加上 "Misty"
        numAnsStr += "Misty";
    }
    
    if (divisibleBy5) {
        // 可被 5 整除，字串加上 "Panther"
        numAnsStr += "Panther";
    }
    
    if (numAnsStr == "") {
        // 皆無法被整除，字串加上該數字本身
        numAnsStr += to_string(i);
    }
    
    // 把計算後的結果加入陣列
    answer[i - 1] = numAnsStr;
}
```

#### 複雜度分析
- 時間複雜度: $O(n)$。
- 空間複雜度: $O(1)$。

### Approach 3: Hash it!

#### 思路
這個算法是上一個算法的再優化。雖然在資料沒有很多時，上個算法感覺已經很好了。但如果今天的題目設計者很邪惡，然後出了一長串的資料怎麼辦？

對於每個數字都寫一個判斷區塊是不可行的，或者說，你的程式最後會有好幾百行然後非常難以閱讀。

再者，倘若今天要改掉其中一個數字，甚至刪除其中一筆資料時，難道我們要把所有區塊都改寫嗎？

其實並不需要。我們可以把資料存在**雜湊表 (Hash Table)** 中。

#### 算法
1. 把所有資料存在一個雜湊表中。如創建 `mistyPantherHash = { 3: "Misty", 5: "Panther" }`。
2. 遍歷整數 `i` 滿足 $1 \le$ `i` $\le n$。
3. 對每個 `i` 遍歷 `mistyPantherHash` 中的每個鍵，判斷其是否可以被整除。
4. 如果步驟 3 成立，那麼把 `mistyPantherHash` 中對應的值加入組合字串中。
5. 把組合字串加入答案陣列。

> 用這種方式你就可以確保，當資料更改時，不需要更改程式內容，只需要更改雜湊表即可。

所以，對於「Misty Panther 8725」來說，它的雜湊表看起來會像是 `{ 3: "Misty", 5: "Panther", 7: "8725" }`

```cpp=
// 以下省略 cin, cout，僅呈現演算法部分
// 答案陣列
string answer[n];

// 雜湊表 mistyPantherDict
unordered_map<int, string> mistyPantherDict = {
    {3, "Misty"}, {5, "Panther"}
};

// 所有除數的陣列
int divisors[] = {3, 5};

for (int i = 1; i <= n; i++) {
    string numAnsStr = "";
    
    for (auto key : divisors) {
        // 若 i 可被 key 整除，就新增對應字串值進 numAnsStr 中
        if (i % key == 0) {
            numAnsStr += mistyPantherDict.at(key);
        }
    }
    
    if (numAnsStr == "") {
        // 皆無法被整除，字串加上該數字本身
        numAnsStr += to_string(i);
    }
    
    // 把計算後的結果加入陣列
    answer[i - 1] = numAnsStr;
}
```

#### 複雜度分析
- 時間複雜度: $O(n)$。
- 空間複雜度: $O(1)$。

### 完整解答
```cpp=
#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
    int n;
    cin >> n;
    unordered_map<int, string> mistyPantherDict = {
        {3, "Misty"}, {5, "Panther"}
    };
	
    int divisors[] = {3, 5};

    for (int i = 1; i <= n; i++) {
        string numAnsStr = "";
    
        for (auto key : divisors) {
            if (i % key == 0) {
                numAnsStr += mistyPantherDict.at(key);
            }
        }
    
        if (numAnsStr == "") {
            numAnsStr += to_string(i);
        }

        cout << numAnsStr << " ";
    }

    cout << endl;
}
```
:::

## Reference
https://leetcode.com/problems/fizz-buzz/