题目描述
难度分:885
输入长度≤60的字符串s,只包含0
,1
和?
。
输入n(1≤n≤1018)。
你需要把s中的?
替换成0
或1
,从而得到一个二进制数x。
问:不超过n的最大x是多少?以十进制形式输出这个最大值。如果不存在这样的x,输出−1。
输入样例1
?0?
2
输出样例1
1
输入样例2
101
4
输出样例2
-1
输入样例3
?0?
1000000000000000000
输出样例3
5
算法
DFS
+剪枝
定义递归函数dfs(i,sum),其中i是当前需要填数的数位,sum是数位[0,i)填完之后的结果。设字符串s的长度为len,索引从0(高位)开始,从高位开始填:
-
如果s[i]≠
?
,那就填数字s[i],继续递归dfs(i+1,sum+s[i]×2len−i−1)。 -
如果s[i]=
?
,那就可以填数了。这里做个贪心,优化一下搜索顺序,为了结果尽可能大,先填1,若后续dfs(i+1,sum+2len−i−1)能得到合法答案,就没有必要填0了,否则还需要在当前数位填0试试。
如果在DFS
的过程中发现sum>n成立,就不用再往深搜了,这条分支肯定搜不到答案。只要到底了,即i≥len,说明中间没有发生过与题目要求冲突的地方,产生一个合法解sum。
复杂度分析
时间复杂度
每个?
的位置可以选填0和1,最差情况下s串全是问号,时间复杂度在O(2n)级别,但由于做了剪枝、并优化了搜索顺序,因此时间复杂度远远低于这个理论的复杂度。
空间复杂度
空间复杂度为递归树的高度,最差情况下应该是O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL n, ans;
int len;
string s;
void dfs(int i, LL sum) {
if(sum > n) return;
if(i >= len) {
ans = sum;
return;
}
if(s[i] == '?') {
dfs(i + 1, sum + (1ll<<(len - i - 1)));
if(ans != -1) return;
dfs(i + 1, sum);
}else {
int d = s[i] - '0';
dfs(i + 1, sum + (1ll<<(len - i - 1))*d);
}
}
int main() {
cin >> s;
len = s.size();
scanf("%lld", &n);
ans = -1;
dfs(0, 0);
printf("%lld\n", ans);
return 0;
}
点个赞