LYY ~ 人生短短几個秋,不醉不回頭。
前言
真可謂是歪打正着,這不就是暴力破解肉口令了嘛 …… 原理是一致的。
實現基礎
二進制模擬
本人博客:
通過二進制串“01”模擬元素取捨進而解決組合問題(數組元素實現排列組合、字符串生成所有子序列、集合生成所有子集)
最長公共子序列
本人博客
助你深刻理解——最長公共子串、最長公共子序列(應該是全網數一數二的比較全面的總結了)
子序列判斷
參考博客:
C++ 沒有 indexOf函數,不過可以通過 find_first_of設計出同樣功能。
因爲原理是不變的:
判斷字符串subSeq中的每一個字符在字符串str中是否存在,
存在的話它們的出現順序是否爲遞增。
// 判斷字符串subSeq中的每一個字符在字符串str中的出現順序是否爲遞增
bool isSubsequence(const string& str, const string& subSeq) {
int index = -1; //設爲-1而不是0,因爲後邊還要 +1;
for (char ch : subSeq) {
index = str.find_first_of(ch, index+1); // 起始位置得是下一位,故需要 +1;
if (index == str.npos) {
return false;
}
}
return true;
}
二進制暴力枚舉
篩選(最長)公共子序列
代碼實現:
#include <iostream>
#include <string>
#include <ctime>
#include <algorithm>
#include <set>
using namespace std;
// 判斷字符串subSeq中的每一個字符在字符串str中的出現順序是否爲遞增
bool isSubsequence(const string& str, const string& subSeq) {
int index = -1; //設爲-1而不是0,因爲後邊還要 +1;
for (char ch : subSeq) {
index = str.find_first_of(ch, index+1); // 起始位置得是下一位,故需要 +1;
if (index == str.npos) {
return false;
}
}
return true;
}
// 2 ^ 64 是上限,故 只能處理母串長度 <64 的情況;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
string s1, s2; // 待輸入的字符串;
while (cin >> s1 >> s2) {
clock_t startTime = clock();
// 確保遍歷的是短字符串的所有子序列,並且是採用剔除元素的方式、自長到短遍歷
if (s1.length() > s2.size()) {
swap(s1, s2);
}
int len = s1.length();
int cont = 1 << len;
string subSeq; // 臨時存儲子序列;
set<string> ss; // 存儲所有公共子序列;
int longest = 0; // 最長公共子序列長度;
bool flag = false; // 公共子序列是否存在;
// i 將會直接影響到選擇子集元素個數的多少(二進制表示);
for (int i = cont-1; i >=0 ; --i) {
for (int j = 0; j < len; ++j) {
if (i & (1 << j)) {
//cout << s1[j];
subSeq += s1[j];
}
}
//cout << "subSeq = " << subSeq << endl;
// 接着判斷 s1 的子序列 subSeq 是否也同時是 s2 的子序列;
if ( isSubsequence(s2,subSeq)) {
ss.insert(subSeq);
if (longest < subSeq.size()) {
longest = subSeq.size();
flag = true; // 不放在 if 語句外邊實爲減少不必要的賦值次數;
}
}
subSeq.clear(); // 記得重置爲空;
}
// 不存在公共序列則輸出空串;
//if (ss.empty()) {// 此判別條件存在漏洞,因爲兩個任意字符串至少存在 空串 作爲公共子序列,故需要立flag;
if(!flag){
cout << endl;
}
else {
for (auto item : ss) {
/*cout << item << endl;*/ // 輸出所有公共子序列;
if (item.length() == longest) {
cout << item << endl;
//break; // 只需要輸出ACSLL最小的最長公共子序列時,break;
}
}
}
cout << "總共耗時:" << double(clock() - startTime) / CLOCKS_PER_SEC << "s" << endl;
}
return 0;
}
測試樣例:
後記
是不是應該再來個(最長)公共非降序子序列、非增序列子序列、公共子串也再來一下?
再說吧。
轉載請註明出,
如需交流可直接評論區留言或者發送私信。
聯繫方式:[email protected]
可發郵件,可加好友,需要等到本人上線。
2019/11/24 01:05
代碼優化補充
這是上邊的代碼:可用於存儲所有公共子序列。
// 接着判斷 s1 的子序列 subSeq 是否也同時是 s2 的子序列;
if ( isSubsequence(s2,subSeq)) {
ss.insert(subSeq);
if (longest < subSeq.size()) {
longest = subSeq.size();
flag = true; // 不放在 if 語句外邊實爲減少不必要的賦值次數;
}
}
若只是要求輸出最長公共子序列,則可以進一步優化代碼,如下所示:
if ( isSubsequence(s2,subSeq)) {
if (longest <= subSeq.size()) {
// 記得改爲 <=符號,否則僅僅會存下一個最長公共子序列,由於子集個數是 2^N,此條件語句篩掉了絕大多數的無關子序列,提高了代碼性能
ss.insert(subSeq);
longest = subSeq.size();
flag = true; // 不放在 if 語句外邊實爲減少不必要的賦值次數;
}
}
推薦相關知識點
算法分析之蠻力法(暴力法)
二進制數及其運算基礎
知識點圖解補充
拓展學習:
查找N個字符串(環)的最長公共子序列
殊途同歸:“DFS+回溯”實現元素取捨模擬並暴力枚舉所有子集
(待更)
2019/11/24 18:10