https://leetcode.cn/problems/cracking-the-safe/
已知:密码有n位,每一位可以从0到k-1中的数中选取
求:解出密码最多要输入多长的字符串?
例:n=2, k=2 密码有2位,每一位可选0或1,则(一种情况)输入00110保证密码正确——输入已包含00、01、10、11所有结果。
思路:构建一个图,每个节点都是n-1位的密码(如n=3,k=2,那么节点为00、01、10、11),每个节点以0-k-1中的数相连(如01通过1与11相连、通过0与10相连),只需由任意一个节点出发遍历所有的节点后回到该节点,遍历过的边加上该节点就是答案。
图源:Tizzi
实现:由某点出发最后回到该点,可以通过BFS实现,用一个集合判断一个节点是否被访问过,当访问的节点已无节点可以访问则说明已回到起点
由于从n-1个全0密码出发,而n位全0密码不需要经过其他路径得到,可以通过连续多次在起点(终点也行,反正是同一个节点)添加0得到,故最后结果要在开头或结尾加上n-1个0。
ps:从0开始的话开头已经有一个0了,这里由于DFS返回的是逆序的结果(当一个节点没有边再添加该节点),故要在开头添加0,在代码中就是在最后加0。
class Solution {
public:
int n, k, mod;
string ans;
set<int> vis; //判断每条边是否访问过
string crackSafe(int N, int K) {
n = N; k = K; mod = pow(10, n - 1);
dfs(0); //从0点出发
ans += string(n - 1, '0'); //添加最开始的0的前缀
return ans;
}
void dfs(int u) {
for (int i = 0; i < k; i++) { //对于每个点循环k条边即可
int v = u * 10 + i; //即边的编号
if (!vis.count(v)) {
vis.insert(v); //标记该边已经访问
dfs(v % mod); //边界,节点最大为n-1位数字
ans += (i+'0'); //添加最后一个数字
cout<<ans<<endl;
}
}
}
};