往事不堪回首,那些年骚操作
最长公共子串篇(20191120)
理论知识:
推荐参考该博文:java实现字符串匹配问题之求两个字符串的最大公共子串
当然这篇也一样,看个人理解:求两个字符串的最长公共子串
图形理解:
矩阵初始化:
矩阵数值演变:
类似算法:
图论中的最短路径算法。
大致分有:迪杰斯特拉算法(Dijkstra)和弗洛伊德算法(Floyd)。
(对应着 贪心算法和动态规划 …… 别慌,名字起的高大尚并无影响理解。。。)
数据结构算法编程课、离散数学课、计算机网络课等都会涉及该算法。
本质都是化作矩阵,故线性代数一定要好好学。
最好的理解方式是什么:
亲自动手 —— 图解,自己手动在草稿纸上推演一遍(小矩阵即可)。
代码实现(C++):
太长时间没写C了,一入python差些找不着回头路(哈哈)
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
typedef vector<string> VS;
// 为避免重复,检查当前子串是否为不存在,不存在时返回true
bool IsNoRepetition(string& , vector<string>& );
int main()
{
string s1, s2; // 待输入的两字符串
string sameSubString; // 临时存储相同子串
VS sameSubStringVector; // 保存所有相同子串
while (cin >> s1 >> s2) {
int max = 0; // 最长子串长度(字符元素个数),初始置0、默认无相同子串
int row = s1.length(); // 矩阵行数(长度)
int col = s2.size(); // 矩阵列数(宽度)
sameSubString.clear(); // 初始化时,需要重置中间存储变量(清空)
// 申请动态二维数组
// 也可以可直接 vector<vector<int>>不过建议多多学习、开拓视野
int** dp = new int *[row];
if (dp) {
for (int i = 0; i < row; ++i) {
dp[i] = new int[col];
}
}
// 初始化矩阵,全部置false(即首先默认字符串不相同,其后相同再+1)
// 此处是为了提醒学弟,注意学习 memset和fill的区别
for (int i = 0; i < row; ++i) {
/*for (int j = 0; j < col; ++j) {
dp[i][j] = 0;
}*/
fill(dp[i], dp[i] + col, 0); // 两种初始化方式
}
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
int iTemp = i, jTemp = j; // 临时变量
while (s1[iTemp] == s2[jTemp]) {
dp[iTemp][jTemp] = dp[iTemp][jTemp] + 1;
sameSubString += s1[iTemp];
iTemp++;
jTemp++;
// 横纵都 +1是为了斜对角线(即 s1和s2串都往后移动一位)
// 值得注意的是别造成数组越界(程序健壮性问题、bug)
if (iTemp == row || jTemp == col) {
break;
}
}
// 相同子串不为空(即存在时)
if (!sameSubString.empty()) {
//cout << "sameSubString = " << sameSubString << endl; // 通过输出测试结果,是否如预期所想
if (IsNoRepetition(sameSubString, sameSubStringVector)) {
sameSubStringVector.push_back(sameSubString);
}
sameSubString.clear(); // 每遍历过一次相同子串,最后记得重置为空(细节)
}
}
}
// 矩阵变换完成后,查找最大值(即为最长相同子串长度)
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (max < dp[i][j]) {
max = dp[i][j];
}
}
}
if (sameSubStringVector.empty()) {
cout << endl;
}
else {
// 将各个相同子串按照字典顺序排序
sort(sameSubStringVector.begin(), sameSubStringVector.end());
for (VS::iterator iter = sameSubStringVector.begin(); iter != sameSubStringVector.end(); ++iter) {
// 直接输出的所有的相同子串
//cout << *iter << endl;
// 使用条件判断只输出最长的相同子串
if ((*iter).size() == max) {
cout << *iter << endl;
break; // break是为了只输出一个最长的公共子串,即ASCLL码最小的那个
}
}
}
//cout << endl; // 此空行是为了排版好看,避免pe格式出错
// 每执行一遍程序,重置为初始状态(为空)。至于,不放在else内,是编码经验释然。
sameSubStringVector.clear();
// new了内存空间就要delete
// 注意这种表达方式
for (int i = 0; i < row; ++i) {
delete[] dp[i];
}
delete[] dp;
}
return 0;
}
bool IsNoRepetition(string& str, vector<string>& vs) {
for (int i = 0; i < vs.size(); ++i) {
if (vs[i] == str)
return false; //有重
}
return true; //无重
}
代码设计满足的要求:
对于每组测试数据,输出最大子串。
如果最大子串为空(即不存在),则输出一个空行。
测试样例:
输入:
abcded123456aabbcc
abcdaa1234
输出:
1234
代码理解:
本人代码