Construct Smallest Number From DI String

Construct Smallest Number From DI String

You are given a 0-indexed string pattern of length n consisting of the characters 'I' meaning increasing and 'D' meaning decreasing.

A 0-indexed string num of length n + 1 is created using the following conditions:

  • num consists of the digits '1' to '9' , where each digit is used at most once.
  • If pattern[i] == 'I' , then num[i] < num[i + 1] .
  • If pattern[i] == 'D' , then num[i] > num[i + 1] .

Return the lexicographically smallest possible string num that meets the conditions.

Example 1:

Input: pattern = "IIIDIDDD"
Output: "123549876"
Explanation:
At indices 0, 1, 2, and 4 we must have that num[i] < num[i+1].
At indices 3, 5, 6, and 7 we must have that num[i] > num[i+1].
Some possible values of num are "245639871", "135749862", and "123849765".
It can be proven that "123549876" is the smallest possible num that meets the conditions.
Note that "123414321" is not possible because the digit '1' is used more than once.

Example 2:

1 Input: pattern = "DDD"
2 Output: "4321"
3 Explanation:
4 Some possible values of num are "9876", "7321", and "8742".
5 It can be proven that "4321" is the smallest possible num that meets the conditions.

Constraints:

$1 \leq pattern.length \leq 8$
pattern consists of only the letters 'I' and 'D' .

 

解题思路

  可以发现方案数很少,只有$9!$个,因此可以直接枚举全排列,找到合法的字典序最小的输出就可以了,因此可以直接从字典序最小的全排列进行枚举。

  AC代码如下,计算量最大为$9!$:

 1 class Solution {
 2 public:
 3     string smallestNumber(string str) {
 4         int n = str.size();
 5         string ret;
 6         for (int i = 1; i <= n + 1; i++) {
 7             ret += (char)(i + '0');
 8         }
 9         
10         do {
11             bool flag = true;
12             for (int i = 0; i < n; i++) {
13                 if (str[i] == 'I' && ret[i] > ret[i + 1]) {
14                     flag = false;
15                     break;
16                 }
17                 if (str[i] == 'D' && ret[i] < ret[i + 1]) {
18                     flag = false;
19                     break;
20                 }
21             }
22             if (flag) break;
23         } while (next_permutation(ret.begin(), ret.end()));
24         
25         return ret;
26     }
27 };

  还有一种贪心解法,时间复杂度为$O(n)$。根据样例模拟可以发现,我们可以根据$“III \dots DDD”$这种组合形式将字符串分成若干组,每一组前部分均为$I$,后部分均为$D$(不一定有$D$)。每一组中的数都是先递增再递减。为了保证字典序最小,每一组中最小的数就是前一组中最大的数加$1$(第$1$组数的最小数为$1$),说白了前面有多少个数那么当前就从下一个数开始选。

  一开始先构造出答案序列$1,2,3,\dots, n+1$,然后扫描每一个$“III \dots DDD”$组合,其中如果是$I$则继续往后枚举,直到遇到$D$,接着往后找到所有连续的一段$D$。假设$D$序列的起始位置为$i$,$D$序列最后一位的下标为$j-1$,那么就把答案序列的$i \sim j$位进行翻转,以此类推。

  AC代码如下:

 1 class Solution {
 2 public:
 3     string smallestNumber(string str) {
 4         int n = str.size();
 5         string ret;
 6         for (int i = 1; i <= n + 1; i++) {
 7             ret += (char)(i + '0');
 8         }
 9         
10         for (int i = 0; i < n; i++) {
11             if (str[i] == 'D') {
12                 int j = i + 1;
13                 while (j < n && str[j] == 'D') {
14                     j++;
15                 }
16                 reverse(ret.begin() + i, ret.begin() + j + 1);
17                 i = j - 1;
18             }
19         }
20         
21         return ret;
22     }
23 };

 

参考资料

  y总,比赛遇到原题了怎么办?力扣第306场周赛:https://www.bilibili.com/video/BV1wv4y1c71n

  【力扣周赛 306】数位 DP 通用模板 | LeetCode 算法刷题:https://www.bilibili.com/video/BV1rS4y1s721

posted @ 2022-08-15 16:59  onlyblues  阅读(54)  评论(0编辑  收藏  举报
Web Analytics