Leetcode 753. Cracking the Safe 雙端佇列實現 給出證明思路
阿新 • • 發佈:2018-11-11
題意
- 我們希望構造一個最短的字串,這個字串每位可以是
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;
}
};