版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/79341677
题意
- 我们希望构造一个最短的字符串,这个字符串每位可以是
0
至k-1
的字符,并且这个字符串的所有n
长子串可以包含所有的 种情况(即包含所有用k
个字符构建的n
长串)
思路
- 不难想象,如果构造的这个串每一个
n
长子串恰好就是一个unique的情况,那么一定就是最短的串,长度为 - 先假设我们一定可以构造出这样的串。然后可以换一个角度来看这个问题,设每一种独特的子串是一个节点,如果一个节点的
n-1
长后缀恰好另一个节点n-1
长的前缀,那么我们就给这两个节点连一条边,因为这两个子串是可以在我们构造的串中直接相连的。这样我就构造了一个有向图,不难证明这个图是一个强连通图,即任意两点之间都是有通路的。那么我们假设的前提,就对应了这个图一定存在一条汉密尔顿通路。而这个图本身不大,我们就完全可以用bfs或者dfs去解这个题了。 - 但是这个假设是否成立却是需要证明的,我参考了Discussion中的方法(Discussion中的原文),这里我给出一个我个人觉得可能更好理解一些的证明。
证明
- 我们想证明上述假设,只需要给出一种构造方法,然后证明这个方法保证可以得到一条哈密尔顿通路
- 首先我们定义一些符号,方便后面说明,我们用小写字母表示一个字符,大写字母加尖括号表示一个
n-1
长的子串,例如a<A>
就表示了后缀为A
的一个n
长子串 - 第二我们给出构造方法:
- (1)随意选一个
n
长子串(或称节点)加入dequeQ
中 - (2)循环,取
Q
中最后一个节点,记为a<A>
,然后选择一个没有加入deque中的节点<A>b
插入队尾 - (3)如果所有形如
<A>b
的节点都在队列中,则弹出队首元素,并把它插入队尾 - (4)循环至队列元素个数到 个
- 这样这个deque中就保存了一条哈密尔顿通路
- (1)随意选一个
- 第三我们证明这个构造方法是正确的,那么需要证明三个事情:首先是序列合法性,即第
i
个节点和第i+1
节点之间应是有边的;二是算法可终止性,即最终一定能得到长度为 的队列;三是节点的独特性,即序列中任意两个节点是不同的。其中第三个是显然保证的,我们主要证明前两点。
- 证明序列合法性,主要就是证明算法第(3)步中队首元素
<B>b
和队尾元素a<A>
,满足A = B
这个条件。出现(3)的情况,对应了 ,也就是说有k
个形如<A>x
的节点已经在队列中了。假如这些节点都没有出现在队首,那么包含队尾元素,应该有k+1
个形如x<A>
的节点出现在队列中,显然这是不可能的,因此,队首元素一定也是形如<A>x
的,那么把它换到队尾,显然可以保证合法性。 - 证明队列
Q
长度可以到达 。反证法,设当Q
长度为m
时,就找不到下一个没有出现过的节点了。根据我们算法的(2)(3)步骤,我们知道当队尾元素找不到下一个节点时,我们会尝试下一个队首元素,如果完全无法找到下一个节点,说明Q
中所有节点都无法连到下一个没有出现过的节点了。这就说明这m
个节点构成了一个联通分量,总体至少有两个联通分量,这和我们一开始知道的这个图是强联通的,应该只有一个联通分量矛盾。因此我们一定可以构造到 长的Q
。 - 得证。
- 证明序列合法性,主要就是证明算法第(3)步中队首元素
实现
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;
}
};