根据模式串构造最小数字

77 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

说在前面

🎈不知道大家对于算法的学习是一个怎样的心态呢?为了面试还是因为兴趣?不管是处于什么原因,算法学习需要持续保持,今天让我们一起来看看这一道题目————根据模式串构造最小数字

题目描述

给你下标从 0 开始、长度为 n 的字符串 pattern ,它包含两种字符,'I' 表示 上升 ,'D' 表示 下降 。

你需要构造一个下标从 0 开始长度为 n + 1 的字符串,且它要满足以下条件:

  • num 包含数字 '1' 到 '9' ,其中每个数字 至多 使用一次。
  • 如果 pattern[i] == 'I' ,那么 num[i] < num[i + 1] 。
  • 如果 pattern[i] == 'D' ,那么 num[i] > num[i + 1] 。

请你返回满足上述条件字典序 最小 的字符串 **num

 

示例 1:

输入: pattern = "IIIDIDDD"
输出: "123549876"
解释: 下标 0124 处,我们需要使 num[i] < num[i+1] 。
下标 3567 处,我们需要使 num[i] > num[i+1] 。
一些可能的 num 的值为 "245639871""135749862""123849765""123549876" 是满足条件最小的数字。
注意,"123414321" 不是可行解因为数字 '1' 使用次数超过 1 次。

示例 2:

输入: pattern = "DDD"
输出: "4321"
解释:
一些可能的 num 的值为 "9876" ,"7321" 和 "8742" 。
"4321" 是满足条件最小的数字。

 

提示:

  • 1 <= pattern.length <= 8
  • pattern 只包含字符 'I' 和 'D' 。

思路分析

首先我们还是要先来理解一下题意,题目会给出一串只包含ID两种字符的字符串,其中'I' 表示 上升,即如果 pattern[i] == 'I' ,那么 num[i] < num[i + 1] ,'D' 表示 下降,即如果 pattern[i] == 'D' ,那么 num[i] > num[i + 1] 。我们需要构造一个下标从 0 开始长度为 n + 1 的字符串,字符串应该满足题目给出参数的升降规则,而且是满足上述条件字典序 最小 的字符串 num

暴力搜索

我们来看一下数据范围:1 <= pattern.length <= 8,最多只有9位数,所以我们其实是可以通过暴力搜索来得出答案的:

回溯模拟,遍历到位置i时,我们查看在1-9中找到一个数,满足pattern[i],继续往下拼接。当遍历到结尾,我们找到了一个满足条件的数字,维护最小的即可。

var smallestNumber = function(pattern) {
    const used = new Array(10).fill(false);
    let ans;
    function dfs(str, i) {
        if (i >= pattern.length) {
            if (!ans || Number(str) < Number(ans)) {
                ans = str
            }
            return;
        }
        const v = pattern[i];
        const prev = str[i];
        for (let j = 1; j < 10; j++) {
            if (used[j]) continue;
            const bool = v === 'I' ? j > +prev : j < +prev;
            if (bool) {
                used[j] = true;
                dfs(str + j, i + 1)
                used[j] = false;
            }
        }
    }
    for (let i = 1; i < 10; i++) {
        used[i] = true;
        dfs(`${i}`, 0)
        used[i] = false;
    }
    return ans;
};

贪心,找规律

那么还有没有其他的方法呢?我们可以从下面几个点入手考虑一下是不是可以使用贪心思想来进行解答。

  • 字符串字典序最小

要使整个字符串的字典序最小,我们应该尽可能的将数值小的数字放在前面。

  • 当前下标位置最大取值

当前位置的最大取值与给出pattern相对位置的字符有关,如果当前位置是I(上升的),我们可以直接取当前没用过的数字的最小值即可,如果是D(下降的),那我们则需要预留出给其下降的空间,如DD,说明它需要连续下降两次,所以我们需要预留出一个数字。也就是说我们当前位置的取值会受到pattern相对位置后面连续D字符的数量的影响,所以我们只需要统计一下连续D的后缀个数即可,具体代码如下:

/**
 * @param {string} pattern
 * @return {string}
 */
 var smallestNumber = function(pattern) {
     let dd = new Array(pattern.length).fill(0);
     for(let j = pattern.length - 1; j >= 0 ; j--){
         if(j == pattern.length - 1) pattern[j] == 'D' ? dd[j] = 1 : '';
         else{
             if(pattern[j] == 'D') dd[j] = dd[j + 1] + 1;
         }
     }
     let res = '',ind = 0;
     for(let i = 0; i < pattern.length; i++){
         res += res.length + 1 + dd[i] - ind;
         if(dd[i]) ind++;
         else ind = 0;
     }
     if(pattern[pattern.length - 1] == 'I'){
         res += res.length + 1;
     }else{
        res += parseInt(res[res.length - 1]) - 1;
     }
     return res;
};

说在后面

🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。