[LeetCode 周賽185] 3. 數青蛙(遞推、分析、巧妙解法)

1. 題目來源

鏈接:5390. 數青蛙

2. 題目說明

在這裏插入圖片描述
在這裏插入圖片描述

3. 題目解析

方法一:遞推+分析+巧妙解法

有一說一確實是一道好題。在此理解這個青蛙數剛好能和最近看的操作系統多線程扯上關係,來自大佬的思想,想想確實很形象。一個線程就輸出一個 "croak",可以連續輸出,但是隻能算作一個線程。思路如下:

  • 創建一個二維 dp[MAXN][5] 數組,即處於字符串 i 位置,處於狀態 c r o a k 的線程有多少個
  • 遍歷字符串
  • 首先將字母轉換爲狀態編號
  • 如果當前狀態爲 0,即代表字符 'c',也就意味着有一個新的線程啓動了,也就是有一個青蛙要開始準備叫了,就需要在該位置進行自增加一
  • 如果當前狀態不爲 0,那麼很顯然就是有一個線程的狀態轉移到了現在這個狀態,那麼就需要對當前狀態加 1,並且需要對前一個狀態進行減 1,完成狀態轉移,即若當前爲 'o',那麼它肯定就是從 'r' 轉移過來的,並不需要關心是哪個線程,只需要關心它進行了狀態轉移即可
  • 用當前的 dp 值是一個增量,再加上上一個 dp 狀態的值就完成了狀態更新。在此不需要管上階段已經蛙叫完畢的,即僅考慮前四個狀態就行了
  • 全過程線程狀態不能小於 0,若小於 0 就返回 -1
  • 當前狀態 dp 求和 sum,並取 max,得到最大的線程數就能解決問題了,這個完全可以參考線程池,即線程池大小僅需要與某時刻最大的線程數相當即可
  • 最後若仍有線程未執行完畢,則出錯,直接返回 -1

上述這個解法建議本地單步調試,就能豁然開朗,寫的確實很冗餘,這個二維的方法可以通過壓縮實現一維的形式。在題解區大多數都是 1 維的直接遍歷解法,思想大同小異,就不多贅述了。

參見代碼如下:

// 執行用時 :76 ms, 在所有 C++ 提交中擊敗了100.00%的用戶
// 內存消耗 :11.3 MB, 在所有 C++ 提交中擊敗了100.00%的用戶

const int MAXN = 1e5 + 50;
int dp[MAXN][5];

int c2i(char x) {
    if (x == 'c') return 0;
    if (x == 'r') return 1;
    if (x == 'o') return 2;
    if (x == 'a') return 3;
    if (x == 'k') return 4;
    return -1;

}

class Solution {
public:
    int minNumberOfFrogs(string str) {
        memset(dp[0], 0, sizeof(dp[0]));

        int n = str.size(), ans = 0;

        for (int i = 1; i <= n; ++i) {
            char c = str[i - 1];
            int cur = c2i(c);
            if (cur == -1) return -1;

            memset(dp[i], 0, sizeof(dp[i]));
            if (cur == 0) dp[i][0]++; 
            else dp[i][cur]++, dp[i][cur - 1]--;

            for (int k = 0; k < 4; ++k) dp[i][k] += dp[i - 1][k];
            for (int k = 0; k < 5; ++k) if (dp[i][k] < 0) return -1;
            int sum = 0;
            for (int k = 0; k < 5; ++k) sum += dp[i][k];
            ans = max(ans, sum);
        }
        for (int k = 0; k < 4; ++k) if (dp[n][k] != 0) return -1;

        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章