【线段树】2213. 由单个字符重复的最长子字符串

简介: 【线段树】2213. 由单个字符重复的最长子字符串

算法可以发掘本质,如:

一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。

二,有无限多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++**实现。

相关文章
|
1月前
|
存储
【题型总结】寻找满足字符出现奇数次/偶数次的子串个数或者子串最长长度
【题型总结】寻找满足字符出现奇数次/偶数次的子串个数或者子串最长长度
52 0
|
1月前
|
索引
leetcode-1624:两个相同字符之间的最长子字符串
leetcode-1624:两个相同字符之间的最长子字符串
25 0
|
1月前
【每日一题Day226】L1156单字符重复子串的最大长度 | 贪心+滑动窗口
【每日一题Day226】L1156单字符重复子串的最大长度 | 贪心+滑动窗口
31 0
|
1月前
|
存储 算法 程序员
【算法训练-字符串 一】【子串问题】最长无重复子串、最长回文子串、最长公共前缀
【算法训练-字符串 一】【子串问题】最长无重复子串、最长回文子串、最长公共前缀
46 0
|
8月前
|
算法
【算法专题突破】双指针 - 无重复字符的最长子串(10)
【算法专题突破】双指针 - 无重复字符的最长子串(10)
24 0
|
算法 前端开发 JavaScript
[LeetCode] 无重复字符的最长子串 & 最长回文子串
博主最近在看新的工作机会,也是在找一些leetcode上比较高频的算法复习一下,这里分享两道算法题的解题。
57 2
[LeetCode] 无重复字符的最长子串 & 最长回文子串
最长不重复子串的有趣解法
最长不重复子串的有趣解法
120 0
剑指offer 49. 最长不含重复字符的子字符串
剑指offer 49. 最长不含重复字符的子字符串
49 0
每日三题-无重复字符的最长子串、最长连续序列、找到字符串中所有字母异位词
每日三题 无重复字符的最长子串 最长连续序列 找到字符串中所有字母异位词
78 1
每日三题-无重复字符的最长子串、最长连续序列、找到字符串中所有字母异位词
|
人工智能
最长连续不重复子串
最长连续不重复子串
110 0
最长连续不重复子串