本身涉及知识点
解析几何 图论 多源路径 贪心
LeetCode1520. 最多的不重叠子字符串
给你一个只包含小写字母的字符串 s ,你需要找到 s 中最多数目的非空子字符串,满足如下条件:
这些字符串之间互不重叠,也就是说对于任意两个子字符串 s[i…j] 和 s[x…y] ,要么 j < x 要么 i > y 。
如果一个子字符串包含字符 char ,那么 s 中所有 char 字符都应该在这个子字符串中。
请你找到满足上述条件的最多子字符串数目。如果有多个解法有相同的子字符串数目,请返回这些子字符串总长度最小的一个解。可以证明最小总长度解是唯一的。
请注意,你可以以 任意 顺序返回最优解的子字符串。
示例 1:
输入:s = “adefaddaccc”
输出:[“e”,“f”,“ccc”]
解释:下面为所有满足第二个条件的子字符串:
[ “adefaddaccc” “adefadda”, “ef”, “e”, “f”, “ccc”, ]
如果我们选择第一个字符串,那么我们无法再选择其他任何字符串,所以答案为 1 。如果我们选择 “adefadda” ,剩下子字符串中我们只可以选择 “ccc” ,它是唯一不重叠的子字符串,所以答案为 2 。同时我们可以发现,选择 “ef” 不是最优的,因为它可以被拆分成 2 个子字符串。所以最优解是选择 [“e”,“f”,“ccc”] ,答案为 3 。不存在别的相同数目子字符串解。
示例 2:
输入:s = “abbaccd”
输出:[“d”,“bb”,“cc”]
解释:注意到解 [“d”,“abba”,“cc”] 答案也为 3 ,但它不是最优解,因为它的总长度更长。
提示:
1 <= s.length <= 105
s 只包含小写英文字母。
分析
多源路径
26个字符看成26个节点,如果字符c1之间有字符c2,则有有向边 c 1 c 2 → \overrightarrow{c1c2}c1c2。利用多源路径看c1到c3是否存在,如果存在则选择c1时,c3也必须选择。令选择c1后,至少选择[l1,r1]。
解析几何
把[i1,r1]看线段,求最多不相交的线段。
贪心
第一条线段,一定是ri最小的,令为t1,显然越小,第二条线越容易找。
第二条线段,一定是ri最小,且li大于t1的。
⋮ \vdots⋮
线段不会交叉,只能包括或相离。所以ri小的,就短。
令a<b<c<d,假定线段:ac和bd相交与bc。
ab中一定有bc中的某个字符,假定其小标为i1,否则ac不会包括bc。bd包括bc,故有此字符,bd必须包括i1,与假设矛盾。
代码
核心代码
//多源码路径 template<class T, T INF = 1000 * 1000 * 1000> class CFloyd { public: CFloyd(const vector<vector<T>>& mat) { m_vMat = mat; const int n = mat.size(); for (int i = 0; i < n; i++) {//通过i中转 for (int i1 = 0; i1 < n; i1++) { for (int i2 = 0; i2 < n; i2++) { //此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离 m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]); //m_vMat[i1][i2] 表示通过[0,i]中转的最短距离 } } } }; vector<vector<T>> m_vMat; }; template<class ELE,class ELE2> void MinSelf(ELE* seft, const ELE2& other) { *seft = min(*seft,(ELE) other); } template<class ELE> void MaxSelf(ELE* seft, const ELE& other) { *seft = max(*seft, other); } class Solution { public: vector<string> maxNumOfSubstrings(string s) { vector<int> indexs[26]; for (int i = 0; i < s.length(); i++) { indexs[s[i] - 'a'].emplace_back(i); } vector<vector<int>> mat(26, vector<int>(26,1000'000'000)); for (int i = 0; i < 26; i++) { if (indexs[i].empty()) { continue; } for (int j = 0; j < 26; j++) { auto it1 = std::lower_bound(indexs[j].begin(), indexs[j].end(), indexs[i].front()); auto it2 = std::upper_bound(indexs[j].begin(), indexs[j].end(), indexs[i].back()); if (it2 - it1 > 0 ) { mat[i][j] = 1; } } } CFloyd floyd(mat); vector<pair<int, int>> rl; for (int i = 0; i < 26; i++) { int left = 1000'000, r = -1; for (int j = 0; j < 26; j++) { if (floyd.m_vMat[i][j] < 1000'000'000) { MinSelf(&left, indexs[j].front()); MaxSelf(&r, indexs[j].back()); } } if (-1 == r) { continue; } rl.emplace_back(r, left); } sort(rl.begin(), rl.end()); int end = -1; vector<string> vAns; for (const auto& [r, left] : rl) { if (left > end) { vAns.emplace_back(s.substr(left, r - left + 1)); end = r; } } return vAns; } };
测试用例
template<class T,class T2> void Assert(const T& t1, const T2& t2) { assert(t1 == t2); } template<class T> void Assert(const vector<T>& v1, const vector<T>& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { string s; { Solution sln; s = "cabcccbaa"; auto res = sln.maxNumOfSubstrings(s); Assert({ "cabcccbaa" }, res); } { Solution sln; s = "ababa"; auto res = sln.maxNumOfSubstrings(s); Assert({ "ababa" }, res); } { Solution sln; s = "adefaddaccc"; auto res = sln.maxNumOfSubstrings(s); Assert({ "e","f","ccc" }, res); } { Solution sln; s = "abbaccd"; auto res = sln.maxNumOfSubstrings(s); Assert({ "bb","cc","d" }, res); } }
2023年5月
class Solution { public: vector maxNumOfSubstrings(string s) { vector<vector> vIndexs(26); int index = 0; for (const char&ch : s) { vIndexs[ch - ‘a’].emplace_back(index++); } vector<std::pair<int, int>> vEndLen; m_vNeib.resize(26); for (int i = 0; i < 26; i++) { const auto& v = vIndexs[i]; if (v.empty()) { continue; } int begin = v.front(); int end = v.back(); Do(begin, end, vIndexs); vEndLen.emplace_back(end, end-begin + 1); } std::sort(vEndLen.begin(), vEndLen.end()); int iEnd = -1; vector vRet; for (int i = 0; i < vEndLen.size(); i++) { const int iBegin = vEndLen[i].first - vEndLen[i].second + 1; if (iBegin > iEnd) { iEnd = vEndLen[i].first; vRet.emplace_back(s.substr(iBegin, vEndLen[i].second)); } } return vRet; } void Do(int& begin, int& end, const vector<vector>& vIndexs) { for (int i = 0; i < 26; i++) { const auto& v = vIndexs[i]; const int iNum = std::upper_bound(v.begin(), v.end(), end) - std::lower_bound(v.begin(), v.end(), begin); if (0 == iNum) { continue; } if ((v.front() < begin) || (v.back() > end)) { begin = min(begin, v.front()); end = max(end, v.back()); Do(begin, end, vIndexs); break; } } } vector<vector> m_vNeib; };
2023年9月
class Solution { public: vector maxNumOfSubstrings(string s) { vector<vector> vIndexs(26); for (int i = 0; i < s.length(); i++) { const int index = s[i] - ‘a’; vIndexs[index].emplace_back(i); } for (int i = 0; i < 26; i++) { if (vIndexs[i].empty()) { continue; } DoRightLeft(vIndexs[i].front(), vIndexs[i].back(), vIndexs); } sort(m_vRightLeft.begin(), m_vRightLeft.end()); int iPreEnd = -1; vector vRet; for (const auto& [r, left] : m_vRightLeft) { const int len = r - left + 1; if (left > iPreEnd) { vRet.emplace_back(s.substr(left, len)); iPreEnd = r; } } return vRet; } void DoRightLeft(int left, int r, const vector<vector>& vIndexs) { bool bAdd = false; for (int i = 0; i < 26; i++) { const auto& v = vIndexs[i]; const int iNum = std::upper_bound(v.begin(), v.end(), r) - std::lower_bound(v.begin(), v.end(), left); if (0 == iNum) { continue; } if ((v.front() < left) || (v.back() > r)) { bAdd = true; left = min(left, v.front()); r = max(r, v.back()); DoRightLeft(left, r, vIndexs); break; } } if (bAdd) { return; } m_vRightLeft.emplace_back(r, left); } vector<pair<int,int>> m_vRightLeft; };
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关
下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。