第三我们证明这个构造方法是正确的,那么需要证明三个事情:首先是序列合法性,即第i个节点和第i+1节点之间应是有边的;二是算法可终止性,即最终一定能得到长度为nk
n
k
的队列;三是节点的独特性,即序列中任意两个节点是不同的。其中第三个是显然保证的,我们主要证明前两点。
证明序列合法性,主要就是证明算法第(3)步中队首元素<B>b和队尾元素a<A>,满足A = B这个条件。出现(3)的情况,对应了 ∀x∈[0,k),<A>x∈Q
∀
x
∈
[
0
,
k
)
,
<
A
>
x
∈
Q
,也就是说有k个形如<A>x的节点已经在队列中了。假如这些节点都没有出现在队首,那么包含队尾元素,应该有k+1个形如x<A>的节点出现在队列中,显然这是不可能的,因此,队首元素一定也是形如<A>x的,那么把它换到队尾,显然可以保证合法性。
证明队列Q长度可以到达nk
n
k
。反证法,设当Q长度为m时,就找不到下一个没有出现过的节点了。根据我们算法的(2)(3)步骤,我们知道当队尾元素找不到下一个节点时,我们会尝试下一个队首元素,如果完全无法找到下一个节点,说明Q中所有节点都无法连到下一个没有出现过的节点了。这就说明这m个节点构成了一个联通分量,总体至少有两个联通分量,这和我们一开始知道的这个图是强联通的,应该只有一个联通分量矛盾。因此我们一定可以构造到nk
n
k
长的Q。
得证。
实现
class Solution {
public:
intpow(int x, int n){
int ret = 1;
for (int i = 0; i < n; i++){
ret *= x;
}
return ret;
}
string crackSafe(int n, int k) {
deque<string> q;
unordered_set<string> mapp;
q.push_back(string(""));
for (int i = 0; i < n; i++){
*q.begin() += '0';
}
mapp.insert(q.back());
int len = pow(k, n) ;
while (q.size() < len){
string now = q.back().substr(1, n - 1);
bool flag = true;
for (char i = '0'; i < k + '0'; i++){
now += i;
if (mapp.find(now) == mapp.end()){
flag = false;
mapp.insert(now);
q.push_back(now);
break;
}
now.pop_back();
}
if (flag){
now = q.front();
q.pop_front();
q.push_back(now);
}
}
string ret;
for (auto& it : q){
ret += it[0];
}
ret.pop_back();
ret += q.back();
return ret;
}
};