Problem: 2999. 统计强大整数的数目
思路
递归 动态规划-记忆化搜索
解题过程
- 假设合法数字s,首先每一位数i上能选择的数字有三个限制
>=start[i]
<=finish[i]
和<=limit
- 但是有些情况下-这三个条件里可以只满足
<=limit
- 例如 在【19,30】 limit=5里 25 是一个合法数字,而此时 5 明显 不符合上面三个条件中其他两个
- 我们得出规律,当
s[i-1]
<finish[i-1]
时 s[i]的取值范围是【0,9】,当s[i-1]
=finish[i-1]
时 s[i]的取值范围是【0,min(finish[i],limit)】,同理当s[i-1]
>start[i-1]
时 s[i]的取值范围是【0,limit】,当s[i-1]
=start[i-1]
时 s[i]的取值范围是`【start[i],min(finish[i],limit)】 - 所以每一次
i
的取值范围都是i-1
取了什么值来确定的 - 设置
dfs(i, low, top)
i
为当前位数,low
为是否限制取最小值top
为是否限制取最大值, 即值为true
的话,表示i-1
选取了一个边界值 - 递归入口是
dfs(0, true, true)
出口是dfs(n, xxx, xxx)
n
为finish
的长度 - 优化:由于有限制的原因 所以取到边界值时结果是不确定的,只有
low
和top
都为 false 时,取值的范围是【0,9】
,此时记录下位数i里的合法情况有多少,再次进入循环i
时可以直接使用记录的数量
复杂度
- 时间复杂度: O()
- 空间复杂度: O(n)
Code
Javascript
var numberOfPowerfulInt = function (start, finish, limit, s) {
if (Number(s) > finish) {
return 0
}
// dfs() 暴力递归 然后记忆之前搜索时 每一步的数量
// 由于 low 和 top 会有限制 所以不能用于所有情况 只记录 都为false 的情况下 有多少结果
// dfs(i, low, top ) i 代表第几位 low 是否要求最低限制 不限制从0开始 top 同理 还有个limit
const n = `${finish}`.length;
const sn = n - s.length;
const has = new Array(n).fill(-1);
let lowarr = `${start}`.padStart(n, "0");
let toparr = `${finish}`.padStart(n, "0");
function dfs(i, low, top) {
if (i == n) {
return 1
}
//当下一次循环进入到 i 时
if (!low && !top && has[i] != -1) {
return has[i];
}
// 计算出能取值范围
let li = low ? lowarr[i] : 0;
let ti = top ? Math.min(toparr[i], limit) : limit;
let result = 0;
if (i < sn) {
for (let j = li; j <= ti; j++) {
// dfs(i + 1, j == lowarr[i] ? low : false, j == toparr[i] ? top : false)
result += dfs(i + 1, j == li && low, j == toparr[i] && top)
}
} else {
// 此时 已经是后缀的范围了 必须使用后缀
const si = s[i - sn];
if (si >= li && si <= ti) {
// dfs(i+1, si == li ? low : false, si == ti ? top : false)
result = dfs(i + 1, si == li && low, si == ti && top)
}
}
if (!low && !top) {
has[i] = result;
}
return result
}
return dfs(0, true, true)
};