Alice has a hand
of cards, given as an array of integers.
Now she wants to rearrange the cards into groups so that each group is size W
, and consists of W
consecutive cards.
Return true
if and only if she can.
Example 1:
Input: hand = [1,2,3,6,2,3,4,7,8], W = 3 Output: true Explanation: Alice'shand
can be rearranged as[1,2,3],[2,3,4],[6,7,8]
Example 2:
Input: hand = [1,2,3,4,5], W = 4 Output: false Explanation: Alice'shand
can't be rearranged into groups of4
Note:
1 <= hand.length <= 10000
0 <= hand[i] <= 10^9
1 <= W <= hand.length
這道題說是我們在打撲克牌,是否能將手里的牌都以順子的形式出完。在打拐3挖坑或者斗地主的時候,順子牌在后期的威力是蠻大的,某些人手里憋了一把牌,結果到后期找個機會上手了之后,直接一把甩完,看的手中只有一把單牌的博主是目瞪口呆。其實到了后期,大家手中都沒啥牌了的時候,就算是很小的連牌,也不一定能要得起,而划單是最沒前途的出法,所以要盡量將手中的牌都組成順子丟出去。這里給了一個W,規定了順子的最小長度,那么我們就拿例子1來模擬下打牌吧,首先摸到了牌之后,肯定要先整牌,按從小到大的順序排列,這里就不考慮啥3最大,4最小啥的,就統一按原始數字排列吧:
1 2 2 3 3 4 6 7 8
好,下面要來組順子,既然這里是3張可連,那么從最小的開始連唄。其實這道題還是簡化了許多,真正打牌的時候,即便是3張起連,那么連4張5張都是可以的,可以這里限定了只能連W張,就使得題目變簡單了。我們用貪婪算法就可以了,首先從1開始,那么一定得有2和3,才能起連,若少了任何一個,都可以直接返回false,好那么取出這三張后,手里還有:
2 3 4 6 7 8
那么從當前手里的最小的牌2開始起連,那么手里必須要有3和4,若少了任何一個,都可以直接返回 false,好那么取出這三張后,手里還有:
6 7 8
從當前手里的最小的牌6開始起連,那么手里必須要有7和8,若少了任何一個,都可以直接返回 false,好那么取出這三張后,手里沒牌了,我們成功的連完了所有的牌。分析這個過程,不難發現,由於牌可以重復,所以要統計每張牌出現的次數,同時還要給牌按大小排序,用 TreeMap 來建立牌的大小和其出現次數之間的映射就最好不過了,利用了其可以按 key 值排序的特點。首先遍歷手中牌,建立映射。然后開始 while 循環,條件是 TreeMap 不為空,然后去除最小的那張牌,然后遍歷能組成順子的W張牌,若沒有直接返回 true,有的話,則映射值自減1,若映射值為0了,則從 TreeMap 中移除該映射對兒即可,while 循環退出后返回 true,參見代碼如下:
解法一:
class Solution { public: bool isNStraightHand(vector<int>& hand, int W) { map<int, int> m; for (int i : hand) ++m[i]; while (!m.empty()) { int start = m.begin()->first; for (int i = 0; i < W; ++i) { if (!m.count(start + i)) return false; if (--m[start + i] == 0) m.erase(start + i); } } return true; } };
我們也可以不對 TreeMap 進行刪除操作,而是直接修改其映射值,在建立好映射對兒之后,不用 while 循環,而是遍歷所有的映射對兒,若某個映射值為0了,直接跳過。然后還是遍歷能組成順子的W張牌,若某張牌出現的次數小於順子起始位置的牌的個數,則直接返回 false,因為肯定會有起始牌剩余,無法全組成順子,這樣還避免了上面解法中一張一張減的操作,提高了運算效率。然后映射值減去起始牌的個數,最后 for 循環退出后,返回 true,參見代碼如下:
解法二:
class Solution { public: bool isNStraightHand(vector<int>& hand, int W) { map<int, int> m; for (int i : hand) ++m[i]; for (auto a : m) { if (a.second == 0) continue; for (int i = a.first; i < a.first + W; ++i) { if (m[i] < a.second) return false; m[i] = m[i] - a.second; } } return true; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/846
參考資料:
https://leetcode.com/problems/hand-of-straights/
https://leetcode.com/problems/hand-of-straights/discuss/135700/Short-Java-solution!
https://leetcode.com/problems/hand-of-straights/discuss/135598/C%2B%2BJavaPython-O(MlogM)-Complexity