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 753. Cracking the Safe 深度优先遍历DFS
- [Leetcode] 753. Cracking the Safe 解题报告
- 每日一题(33)——用两个栈实现一个队列的功能?要求给出算法和思路
- Leetcode 225 Implement Stack using Queues 使用队列实现栈
- LeetCode 232 Implement Queue using Stacks(利用栈实现队列)
- **php队列的实现思路和详细过程
- 两个栈实现一个队列的思路
- 双端链表实现队列
- 双端队列(dequeue)链表实现
- LeetCode(23)-Implement Queue using Stacks(栈实现队列)
- LeetCode 225 Implement Stack using Queues(利用队列实现栈)
- Leetcode 225,232 用两个队列实现栈,用两个栈实现队列
- [LeetCode] Implement Queue using Stacks 用栈来实现队列
- 后端利用Redis队列及哈希实现定时推送提醒的三个思路
- [疯狂Java]集合:Deque(双端队列)以及两个实现(ArrayDeque、LinkedList)、Stack(摒弃)、各线性表性能分析
- Leetcode 45. Jump Game II dp优化 (给出了5种思路)
- Leetcode232. Implement Queue using Stacks[栈实现队列]
- POJ-2823--Sliding Window--双端队列实现单调队列
- LeetCode学习之-225. 利用队列实现堆栈(Implement Stack using Queues)