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;
}
};