您的位置:首页 > Web前端

Leetcode 753. Cracking the Safe 双端队列实现 给出证明思路

2018-02-21 03:46 316 查看

题意

我们希望构造一个最短的字符串,这个字符串每位可以是
0
k-1
的字符,并且这个字符串的所有
n
长子串可以包含所有的nknk种情况(即包含所有用
k
个字符构建的
n
长串)

思路

不难想象,如果构造的这个串每一个
n
长子串恰好就是一个unique的情况,那么一定就是最短的串,长度为nk+n−1nk+n−1

先假设我们一定可以构造出这样的串。然后可以换一个角度来看这个问题,设每一种独特的子串是一个节点,如果一个节点的
n-1
长后缀恰好另一个节点
n-1
长的前缀,那么我们就给这两个节点连一条边,因为这两个子串是可以在我们构造的串中直接相连的。这样我就构造了一个有向图,不难证明这个图是一个强连通图,即任意两点之间都是有通路的。那么我们假设的前提,就对应了这个图一定存在一条汉密尔顿通路。而这个图本身不大,我们就完全可以用bfs或者dfs去解这个题了。

但是这个假设是否成立却是需要证明的,我参考了Discussion中的方法(Discussion中的原文),这里我给出一个我个人觉得可能更好理解一些的证明。

证明

我们想证明上述假设,只需要给出一种构造方法,然后证明这个方法保证可以得到一条哈密尔顿通路

首先我们定义一些符号,方便后面说明,我们用小写字母表示一个字符,大写字母加尖括号表示一个
n-1
长的子串,例如
a<A>
就表示了后缀为
A
的一个
n
长子串

第二我们给出构造方法:

(1)随意选一个
n
长子串(或称节点)加入deque
Q


(2)循环,取
Q
中最后一个节点,记为
a<A>
,然后选择一个没有加入deque中的节点
<A>b
插入队尾

(3)如果所有形如
<A>b
的节点都在队列中,则弹出队首元素,并把它插入队尾

(4)循环至队列元素个数到nknk个

这样这个deque中就保存了一条哈密尔顿通路

第三我们证明这个构造方法是正确的,那么需要证明三个事情:首先是序列合法性,即第
i
个节点和第
i+1
节点之间应是有边的;二是算法可终止性,即最终一定能得到长度为nknk的队列;三是节点的独特性,即序列中任意两个节点是不同的。其中第三个是显然保证的,我们主要证明前两点。

证明序列合法性,主要就是证明算法第(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
长度可以到达nknk。反证法,设当
Q
长度为
m
时,就找不到下一个没有出现过的节点了。根据我们算法的(2)(3)步骤,我们知道当队尾元素找不到下一个节点时,我们会尝试下一个队首元素,如果完全无法找到下一个节点,说明
Q
中所有节点都无法连到下一个没有出现过的节点了。这就说明这
m
个节点构成了一个联通分量,总体至少有两个联通分量,这和我们一开始知道的这个图是强联通的,应该只有一个联通分量矛盾。因此我们一定可以构造到nknk长的
Q


得证。

实现

class Solution {
public:
int pow(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;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode 算法题