846. Hand of Straights。

探讨了一种算法,用于判断一组整数是否能被分成多个连续的子数组,每个子数组长度固定,通过使用Map和余数技巧实现。

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’s hand 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’s hand can’t be rearranged into groups of 4.

Note:

  • 1 <= hand.length <= 10000
  • 0 <= hand[i] <= 10^9
  • 1 <= W <= hand.length

原文链接:https://leetcode.com/problems/hand-of-straights/


给定一个数组和一个W,需要我们将该数组分成若干组,需要满足每一组有W个数字元素,并且W个数字必须连续。


刚开始我的思路比较简单,首先把数组中所有的元素放到一个Map中,数组的元素作为Map中的key,该元素出现的次数作为Map的value。以官方例子中的数组:[1,2,3,6,2,3,4,7,8] 。

在这里插入图片描述

放到Map中后,首先用数组的总个数除以W,得到的n就是最后一共能够分为多少组,当然如果不能整除的话就直接返回false即可。按照题意,每一个分组的数字都是连续的,我们可以先找到Map中第一个value不为0的元素(value代表该元素可用于进行分组的个数),然后如果能够满足题意的话,那么该元素接下来的W个元素在Map中存储的个数都是大于0的,并且遍历一次元素就代表对该元素进行了分组,所以需要将其value减去一。如果不满足题意的话,就会遇到某个元素的value为0,这时直接返回false即可。

将例子中进行第一次遍历分组,可以将 {1,2,3} 分出来:

在这里插入图片描述

进行第二次分组,将 {2,3,4}分出来:

在这里插入图片描述

第三次也就是最后一次,将 {6,7,8} 分出来:

在这里插入图片描述

如果其中不满足的话,就会出现value等于0的情况。

class Solution {
public:
    bool isNStraightHand(vector<int>& hand, int W) {
        if(hand.size()==0 || hand.size()%W!=0)
            return false;

        map<int,int> PAIR;

        for(int num : hand) {//将数组中的元素放到map中
            PAIR[num]++;
        }

        map<int,int>::iterator it = PAIR.begin();
        //while(it != PAIR.end()){
            //cout << "first:" << it->first << ",second:" << it->second << endl;
            //it++;
        //}

        int i=0,n=hand.size()/W;
        int start,end;
        while(i < n) {
            while(it != PAIR.end()) {// 找到最小的没有分配过的元素
                if(it->second > 0) {
                    start = it->first;
                    break;
                }
                it++;
            }
            //cout << "start:" << start << endl;

            end = start + W;

            while(start < end) { // 接下来的W个应该在map中都有存放
                //cout << start << " " << "," << PAIR[start] << endl;
                if(PAIR[start]>0){ // 如果大于0则说明该元素还有剩余,说明可以用来组成连续的数组
                    PAIR[start]--; // 使用一次之后,将该元素个数减1
                    start++;// 因为要求是连续的,所以start++就是代表下一个应该出现的数字
                } else {
                    return false;
                }
            }
            //cout << endl;
            i++;
        }

        return true;
    }
};

看到提交的里面有一种类似的思路,比较简洁但是思路差不多:

class Solution {
public:
    bool isNStraightHand(vector<int>& hand, int W) {
        
        map<int,int> m;
        for(auto h : hand)
        {
            m[h]++;
        }
        
        
        for(auto i : m)
        {
            if(i.second == 0)
            {
                continue;
            }
            for(int j = i.first+W-1 ; j >= i.first ; j--)
            {
                m[j]-=i.second;
                if(m[j] < 0)
                {
                    return false;
                }
            }
        }
        return true;
    }
};

另一种比较精妙的思路,既然是分为多个分组,每个分组有W个元素。可以用数组中的元素对W取余数,可以将数组中的所有元素都归到0到W-1中,比如例子中的 :[1,2,3,6,2,3,4,7,8] , 分别对W=3取余数,那么全部可以归成 {0,1,2},这样我们只需要统计 {0,1,2}的出现次数是否相等即可,如果相等则说明可以划分为若干个W长度的分组,否则不可以。

class Solution {
public:
    bool isNStraightHand(vector<int>& hand, int W) {
        int n = hand.size();
        if (n < 1 || W < 1 || n < W)
        {
            return false;
        }

        if (n % W != 0)
        {
            return false;
        }

        vector<int> count (W, 0);
        for (const int& h : hand)
        {
            ++count[h % W]; // 将所有的连续数列压缩到 0 到 W-1 之间,这样0到W-1之间的数字出现的次数都一样
        }

        int expect = count.front();// 找出0出现的次数,后面的次数都应该等于这个
        for(const int& c : count)
        {
            if (c != expect)
            {
                return false;
            }
        }

        return true;
    }
};
``` #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义扑克牌与其数值的映射关系结构体 typedef struct { char card[3]; // 扑克牌面(考虑到"10"有两个字符,所以数组大小为3) int value; // 对应的数值 } CardMap; // 扑克牌面到数值的映射表 CardMap card_to_number[] = { {"3", 3}, {"4", 4}, {"5", 5}, {"6", 6}, {"7", 7}, {"8", 8}, {"9", 9}, {"10", 10}, {"J", 11}, {"Q", 12}, {"K", 13}, {"A", 14}, {"2", 16} }; int card_map_size = sizeof(card_to_number) / sizeof(card_to_number[0]); // 用于比较扑克牌的函数 int compare_cards(const void *a, const void *b) { const char *card1 = *(const char **)a; const char *card2 = *(const char **)b; int value1 = 0, value2 = 0; for (int i = 0; i < card_map_size; ++i) { if (strcmp(card_to_number[i].card, card1) == 0) { value1 = card_to_number[i].value; } if (strcmp(card_to_number[i].card, card2) == 0) { value2 = card_to_number[i].value; } } return value1 - value2; } int main() { char input[100]; // 存储输入字符串 char *cards[20]; // 存储分割后的扑克牌字符串指针 int count = 0; // 扑克牌数量 // 读取一行输入 fgets(input, sizeof(input), stdin); input[strcspn(input, "\n")] = 0; // 移除换行符 // 分割输入字符串 char *token = strtok(input, " "); while (token != NULL) { cards[count++] = token; token = strtok(NULL, " "); } // 对扑克牌进行排序 qsort(cards, count, sizeof(char *), compare_cards); // 动态分配二维数组来存储可能的顺子序列 char **straights[count]; int lengths[count]; // 存储每个顺子的长度 for (int i = 0; i < count; ++i) { straights[i] = (char **)malloc(sizeof(char) * count); straights[i][0] = cards[i]; lengths[i] = 1; } int num_straights = count; // 生成顺子序列 for (int i = 0; i < count; ++i) { for (int j = 0; j < num_straights; ++j) { if (compare_cards(&cards[i], &straights[j][lengths[j] - 1]) == 1) { straights[j][lengths[j]++] = cards[i]; break; } } } // 输出长度至少为5的顺子序列 int found = 0; for (int i = 0; i < num_straights; ++i) { if (lengths[i] >= 5) { found = 1; for (int j = 0; j < lengths[i]; ++j) { printf("%s ", straights[i][j]); } printf("\n"); } free(straights[i]); } if (!found) { printf("No\n"); } return 0; }```我希望输入:2 9 J 10 3 4 K A 7 Q A 5 6;输出的结果:3 4 5 6 7 9 10 J Q K A;修改这个代码使得输入输出满足
最新发布
03-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值