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