算法可以发掘本质,如:
一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。
二,有无限多1X2和2X1的骨牌,某个棋盘若干格子坏了,如何在没有坏的格子放足够多骨牌。
三,某个单色图,1表示前前景,0表示后景色。每次操作可以将一个1,变成0。如何在最少得操作情况下,使得没有两个1相邻(四连通)。
四,若干路人,有些人是熟人,如何选出最多的人参加实验。为了避免熟人影响实验的效果,参加的人不能是熟人。
一二是二分图的最大匹配,三是二分图的最小点覆盖,四是二分图最大独立集。 而这三者是等效问题。
本文涉及知识点
LeetCode 2213. 由单个字符重复的最长子字符串
给你一个下标从 0 开始的字符串 s 。另给你一个下标从 0 开始、长度为 k 的字符串 queryCharacters ,一个下标从 0 开始、长度也是 k 的整数 下标 数组 queryIndices ,这两个都用来描述 k 个查询。
第 i 个查询会将 s 中位于下标 queryIndices[i] 的字符更新为 queryCharacters[i] 。
返回一个长度为 k 的数组 lengths ,其中 lengths[i] 是在执行第 i 个查询 之后 s 中仅由 单个字符重复 组成的 最长子字符串 的 长度 。
示例 1:
输入:s = “babacc”, queryCharacters = “bcb”, queryIndices = [1,3,3]
输出:[3,3,4]
解释:
- 第 1 次查询更新后 s = “bbbacc” 。由单个字符重复组成的最长子字符串是 “bbb” ,长度为 3 。
- 第 2 次查询更新后 s = “bbbccc” 。由单个字符重复组成的最长子字符串是 “bbb” 或 “ccc”,长度为 3 。
- 第 3 次查询更新后 s = “bbbbcc” 。由单个字符重复组成的最长子字符串是 “bbbb” ,长度为 4 。
因此,返回 [3,3,4] 。
示例 2:
输入:s = “abyzz”, queryCharacters = “aa”, queryIndices = [2,1]
输出:[2,3]
解释:
- 第 1 次查询更新后 s = “abazz” 。由单个字符重复组成的最长子字符串是 “zz” ,长度为 2 。
- 第 2 次查询更新后 s = “aaazz” 。由单个字符重复组成的最长子字符串是 “aaa” ,长度为 3 。
因此,返回 [2,3] 。
提示:
1 <= s.length <= 105
s 由小写英文字母组成
k == queryCharacters.length == queryIndices.length
1 <= k <= 105
queryCharacters 由小写英文字母组成
0 <= queryIndices[i] < s.length
用向量实现的线段树
template<class TSave> class CVectorSave { public: CVectorSave(int iMinIndex,int iMaxIndex, TSave tDefault) :m_iMinIndex(iMinIndex), m_iMaxIndex(iMaxIndex){ m_vec.assign((iMaxIndex-iMinIndex+1)*4, tDefault); } TSave& Ref(int iNodeNO) { return m_vec[iNodeNO]; } const int m_iMinIndex, m_iMaxIndex; protected: vector<TSave> m_vec; }; template<class TSave, class TRecord,class TSaveCon = CVectorSave<TSave> > class CSingUpdateLineTree { public: CSingUpdateLineTree(int iEleSize, TSave tDefault) :m_vSave(0,iEleSize-1, tDefault){ } CSingUpdateLineTree(int iMinIndex, int iMaxIndex, TSave tDefault) :m_vSave(iMinIndex, iMaxIndex, tDefault) { } void Update(int index, TRecord update) { Update(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex, index, update); } void Query(int leftIndex, int leftRight) { Query(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex, leftIndex, leftRight); } void Init() { Init(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex); } TSave QueryAll() { return m_vSave.Ref(1); } protected: void Init(int iNodeNO, int iSaveLeft, int iSaveRight) { if (iSaveLeft == iSaveRight) { OnInit(m_vSave.Ref(iNodeNO), iSaveLeft); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; Init(iNodeNO * 2, iSaveLeft, mid); Init(iNodeNO * 2 + 1, mid + 1, iSaveRight); OnUpdateParent(m_vSave.Ref(iNodeNO), m_vSave.Ref(iNodeNO*2), m_vSave.Ref(iNodeNO*2+1), iSaveLeft, iSaveRight); } void Query(int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) { if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) { OnQuery(m_vSave.Ref(iNodeNO)); return; } if (iSaveLeft == iSaveRight) {//没有子节点 return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (mid >= iQueryLeft) { Query(iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight); } if (mid + 1 <= iQueryRight) { Query(iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight); } } void Update(int iNodeNO, int iSaveLeft, int iSaveRight, int iUpdateNO, TRecord update) { if (iSaveLeft == iSaveRight) { OnUpdate(m_vSave.Ref(iNodeNO), iSaveLeft, update); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (iUpdateNO <= mid) { Update(iNodeNO * 2, iSaveLeft, mid, iUpdateNO, update); } else { Update(iNodeNO * 2 + 1, mid + 1, iSaveRight, iUpdateNO, update); } OnUpdateParent(m_vSave.Ref(iNodeNO), m_vSave.Ref(iNodeNO*2), m_vSave.Ref(iNodeNO*2+1), iSaveLeft, iSaveRight); } virtual void OnInit(TSave& save, int iSave) = 0; virtual void OnQuery(TSave& save) = 0; virtual void OnUpdate(TSave& save,int iSaveLeft, const TRecord& update) = 0; virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) = 0; CVectorSave<TSave> m_vSave; }; template<class TSave = std::tuple<int,int,int>, class TRecord = char, class TSaveCon = CVectorSave<TSave> > class CMyLineTree :public CSingUpdateLineTree<TSave,TRecord> { public: CMyLineTree(const string& s) :m_s(s), CSingUpdateLineTree<TSave, TRecord>(s.length(), { 0,0,0 }) { } void Update(int index, TRecord update) { m_s[index] = update; CSingUpdateLineTree<TSave, TRecord>::Update(index, update); } protected: virtual void OnInit(TSave& save, int iSave) override { save = { 1,1,1 }; } virtual void OnQuery(TSave& save) override { } virtual void OnUpdate(TSave& save, int iSaveLeft, const TRecord& update) override { save = { 1,1,1 }; } virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) override { int i1 = get<0>(left);//最长前缀 int i2 = max(get<1>(left), get<1>(r));//最长字符串 int i3 = get<2>(r);//最长后缀 const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (m_s[mid] == m_s[mid + 1]) {//拼接 i2 = max(i2, get<2>(left) + get<0>(r)); if (mid - iSaveLeft + 1 == i1) { i1 += get<0>(r); } if (iSaveRight - mid == i3) { i3 += get<2>(left); } } par = { i1,i2,i3 }; } string m_s; }; class Solution { public: vector<int> longestRepeating(string s, string queryCharacters, vector<int>& queryIndices) { CMyLineTree lineTree(s); lineTree.Init(); vector<int> vRet; for (int i = 0; i < queryCharacters.size(); i++) { lineTree.Update(queryIndices[i], queryCharacters[i]); const auto [i1, i2, i3] = lineTree.QueryAll(); vRet.emplace_back(i2); } return vRet; } };
用哈希映射实现
template<class TSave> class CUnorderMapSave { public: CUnorderMapSave(int iMinIndex, int iMaxIndex, TSave tDefault) :m_iMinIndex(iMinIndex), m_iMaxIndex(iMaxIndex), m_tDefault(tDefault){ } TSave& Ref(int iNodeNO) { if (!m_map.count(iNodeNO)) { m_map[iNodeNO] = m_tDefault; } return m_map[iNodeNO]; } const int m_iMinIndex, m_iMaxIndex; protected: const TSave m_tDefault; unordered_map<int, TSave> m_map; }; template<class TSave, class TRecord,class TSaveCon > class CSingUpdateLineTree { public: CSingUpdateLineTree(int iEleSize, TSave tDefault) :m_vSave(0,iEleSize-1, tDefault){ } CSingUpdateLineTree(int iMinIndex, int iMaxIndex, TSave tDefault) :m_vSave(iMinIndex, iMaxIndex, tDefault) { } void Update(int index, TRecord update) { Update(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex, index, update); } void Query(int leftIndex, int leftRight) { Query(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex, leftIndex, leftRight); } void Init() { Init(1, m_vSave.m_iMinIndex, m_vSave.m_iMaxIndex); } TSave QueryAll() { return m_vSave.Ref(1); } protected: void Init(int iNodeNO, int iSaveLeft, int iSaveRight) { if (iSaveLeft == iSaveRight) { OnInit(m_vSave.Ref(iNodeNO), iSaveLeft); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; Init(iNodeNO * 2, iSaveLeft, mid); Init(iNodeNO * 2 + 1, mid + 1, iSaveRight); OnUpdateParent(m_vSave.Ref(iNodeNO), m_vSave.Ref(iNodeNO*2), m_vSave.Ref(iNodeNO*2+1), iSaveLeft, iSaveRight); } void Query(int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) { if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) { OnQuery(m_vSave.Ref(iNodeNO)); return; } if (iSaveLeft == iSaveRight) {//没有子节点 return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (mid >= iQueryLeft) { Query(iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight); } if (mid + 1 <= iQueryRight) { Query(iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight); } } void Update(int iNodeNO, int iSaveLeft, int iSaveRight, int iUpdateNO, TRecord update) { if (iSaveLeft == iSaveRight) { OnUpdate(m_vSave.Ref(iNodeNO), iSaveLeft, update); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (iUpdateNO <= mid) { Update(iNodeNO * 2, iSaveLeft, mid, iUpdateNO, update); } else { Update(iNodeNO * 2 + 1, mid + 1, iSaveRight, iUpdateNO, update); } OnUpdateParent(m_vSave.Ref(iNodeNO), m_vSave.Ref(iNodeNO*2), m_vSave.Ref(iNodeNO*2+1), iSaveLeft, iSaveRight); } virtual void OnInit(TSave& save, int iSave) = 0; virtual void OnQuery(TSave& save) = 0; virtual void OnUpdate(TSave& save,int iSaveLeft, const TRecord& update) = 0; virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) = 0; TSaveCon m_vSave; }; template<class TSave = std::tuple<int,int,int>, class TRecord = char, class TSaveCon = CUnorderMapSave<TSave> > class CMyLineTree :public CSingUpdateLineTree<TSave,TRecord, TSaveCon> { public: CMyLineTree(const string& s) :m_s(s), CSingUpdateLineTree<TSave, TRecord, TSaveCon>(s.length() ,{ 0,0,0 }) { } void Update(int index, TRecord update) { m_s[index] = update; CSingUpdateLineTree<TSave, TRecord, TSaveCon>::Update(index, update); } protected: virtual void OnInit(TSave& save, int iSave) override { save = { 1,1,1 }; } virtual void OnQuery(TSave& save) override { } virtual void OnUpdate(TSave& save, int iSaveLeft, const TRecord& update) override { save = { 1,1,1 }; } virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) override { int i1 = get<0>(left);//最长前缀 int i2 = max(get<1>(left), get<1>(r));//最长字符串 int i3 = get<2>(r);//最长后缀 const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (m_s[mid] == m_s[mid + 1]) {//拼接 i2 = max(i2, get<2>(left) + get<0>(r)); if (mid - iSaveLeft + 1 == i1) { i1 += get<0>(r); } if (iSaveRight - mid == i3) { i3 += get<2>(left); } } par = { i1,i2,i3 }; } string m_s; }; class Solution { public: vector<int> longestRepeating(string s, string queryCharacters, vector<int>& queryIndices) { CMyLineTree lineTree(s); lineTree.Init(); vector<int> vRet; for (int i = 0; i < queryCharacters.size(); i++) { lineTree.Update(queryIndices[i], queryCharacters[i]); const auto [i1, i2, i3] = lineTree.QueryAll(); vRet.emplace_back(i2); } return vRet; } };
再次修改封装类
template<class TSave, class TRecord > class CSingUpdateLineTree { protected: virtual void OnInit(TSave& save, int iSave) = 0; virtual void OnQuery(TSave& save) = 0; virtual void OnUpdate(TSave& save, int iSaveLeft, const TRecord& update) = 0; virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) = 0; }; template<class TSave, class TRecord > class CVectorSingUpdateLineTree : public CSingUpdateLineTree<TSave, TRecord> { public: CVectorSingUpdateLineTree(int iEleSize, TSave tDefault) :m_iEleSize(iEleSize),m_vSave(iEleSize*4,tDefault){ } void Update(int index, TRecord update) { Update(1, 0, m_iEleSize-1, index, update); } void Query(int leftIndex, int leftRight) { Query(1, 0, m_iEleSize - 1, leftIndex, leftRight); } void Init() { Init(1, 0, m_iEleSize - 1); } TSave QueryAll() { return m_vSave[1]; } protected: int m_iEleSize; void Init(int iNodeNO, int iSaveLeft, int iSaveRight) { if (iSaveLeft == iSaveRight) { this->OnInit(m_vSave[iNodeNO], iSaveLeft); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; Init(iNodeNO * 2, iSaveLeft, mid); Init(iNodeNO * 2 + 1, mid + 1, iSaveRight); this->OnUpdateParent(m_vSave[iNodeNO], m_vSave[iNodeNO*2], m_vSave[iNodeNO*2+1], iSaveLeft, iSaveRight); } void Query(int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) { if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) { this->OnQuery(m_vSave[iNodeNO]); return; } if (iSaveLeft == iSaveRight) {//没有子节点 return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (mid >= iQueryLeft) { Query(iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight); } if (mid + 1 <= iQueryRight) { Query(iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight); } } void Update(int iNodeNO, int iSaveLeft, int iSaveRight, int iUpdateNO, TRecord update) { if (iSaveLeft == iSaveRight) { this->OnUpdate(m_vSave[iNodeNO], iSaveLeft, update); return; } const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (iUpdateNO <= mid) { Update(iNodeNO * 2, iSaveLeft, mid, iUpdateNO, update); } else { Update(iNodeNO * 2 + 1, mid + 1, iSaveRight, iUpdateNO, update); } this->OnUpdateParent(m_vSave[iNodeNO], m_vSave[iNodeNO*2], m_vSave[iNodeNO*2+1], iSaveLeft, iSaveRight); } vector<TSave> m_vSave; }; template<class TSave = std::tuple<int, int, int>, class TRecord = char > class CMyLineTree :public CVectorSingUpdateLineTree<TSave, TRecord> { public: CMyLineTree(const string& s) :m_s(s), CVectorSingUpdateLineTree<TSave, TRecord>(s.length(), { 0,0,0 }) { } void Update(int index, TRecord update) { m_s[index] = update; CVectorSingUpdateLineTree<TSave, TRecord>::Update(index, update); } protected: string m_s; virtual void OnInit(TSave& save, int iSave) override { save = { 1,1,1 }; } virtual void OnQuery(TSave& save) override { } virtual void OnUpdate(TSave& save, int iSaveLeft, const TRecord& update) override { save = { 1,1,1 }; } virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) override { int i1 = get<0>(left);//最长前缀 int i2 = max(get<1>(left), get<1>(r));//最长字符串 int i3 = get<2>(r);//最长后缀 const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2; if (m_s[mid] == m_s[mid + 1]) {//拼接 i2 = max(i2, get<2>(left) + get<0>(r)); if (mid - iSaveLeft + 1 == i1) { i1 += get<0>(r); } if (iSaveRight - mid == i3) { i3 += get<2>(left); } } par = { i1,i2,i3 }; } }; class Solution { public: vector<int> longestRepeating(string s, string queryCharacters, vector<int>& queryIndices) { CMyLineTree lineTree(s); lineTree.Init(); vector<int> vRet; for (int i = 0; i < queryCharacters.size(); i++) { lineTree.Update(queryIndices[i], queryCharacters[i]); const auto [i1, i2, i3] = lineTree.QueryAll(); vRet.emplace_back(i2); } return vRet; } };
测试用例
template<class T> void Assert(const T& t1, const T& 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, queryCharacters; vector<int> queryIndices; { s = "babacc", queryCharacters = "bcb", queryIndices = {1,3,3}; auto res = Solution().longestRepeating(s, queryCharacters, queryIndices); Assert({ 3,3,4 }, res); } { s = "abyzz", queryCharacters = "aa", queryIndices = { 2, 1 }; auto res = Solution().longestRepeating(s, queryCharacters, queryIndices); Assert({ 2,3 }, res); } //CConsole::Out(res); }
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。