1. 题目来源
链接:5375. 恢复数组
2. 题目说明
3. 题目解析
方法一:dp+巧妙解法
貌似是一道很经典的 DP
问题。因为一般涉及到 计数,基本就需要首先考虑动态规划了。思路如下:
- 状态定义:
dp[i]
表示前i
位组成若干个合法数字的方案数 - 状态转移:
dp[i] += dp[j - 1]
,表示字符串s[j.....i]
是一个合法数字,即前j - 1
个数字组成dp[j - 1]
个方案数,现在又把第i
位字符接到后面组成合法数字,那么能接收所有的dp[j - 1]
的方案数,所有能够直接相加
那么在编写程序时需要注意以下几点:
- 用
cur
计算当前数字大小,这里就需要将字符串转数字,就从后向前进行遍历,再乘上 10 的进位就行了 - 注意前导零,即首位是 0 的情况进行判断,如果出现 0 了,那么直接
continue
就行了,但是要注意这个flag
是要进行变化的。因为已经跨越了一个数位了 - 注意当前数字是否在范围
k
内。若不存在则continue
建议画画字符串位的区间,就懂了。一定得动手,或者限定 15min
进行思考,不是很清晰再进行纸笔举例模拟。一味的空想会浪费很多时间。
参见代码如下:
// 执行用时 :4 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :5.9 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
string getHappyString(int n, int k) {
int lim = 1, cnt = 0;
for (int i = 0; i < n; ++i) lim *= 3;
for (int i = 0; i < lim; ++i) {
bool flag = true;
for (int j = 1, k = i, last = 3; j <= n and flag; ++j) {
int cur = k % 3; k /= 3;
cout << cur << endl;
if (last == cur) flag = false;
last = cur;
}
if (flag) ++cnt;
if (cnt == k) {
string ans = "";
for (int m = 1, v = i; m <= n; ++m) {
int cur = v % 3; v /= 3;
ans = (char)('a' + cur) + ans;
}
return ans;
}
}
return "";
}
};
4. 双周赛总结
这阵子刷题少,整项目和理论居多,不免手生。前两道也算顺顺利利,第三道一直再思考数学解法,导致头脑一片昏沉…然后转为全排列磨磨唧唧的才做出来。
不过好像第三道确实可以采用数学解法进行求解:第一位有三种,后面的都只有 2 种情况,总的情况是 3 ∗ 2 ( n − 1 ) 3*2^{(n-1)} 3∗2(n−1)。可以直接算出来每一位是什么符号,复杂度是 O ( n ) O(n) O(n)。 貌似听大佬讲说是有点康托逆展开的感觉?哈哈,算法就是这么奇妙有趣。