【解析几何】 【多源路径】 【贪心】1520 最多的不重叠子字符串

简介: 【解析几何】 【多源路径】 【贪心】1520 最多的不重叠子字符串

本身涉及知识点

解析几何 图论 多源路径 贪心

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 只包含小写英文字母。

分析

image.png

多源路径

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++**实现。

相关文章
|
7月前
|
JavaScript 前端开发 Java
正则表达式深度解析:匹配任意字符串
【4月更文挑战第1天】
3417 0
|
3月前
|
JavaScript
js 解析 byte数组 成字符串
js 解析 byte数组 成字符串
84 5
|
4月前
|
存储 关系型数据库 MySQL
|
5月前
|
SQL 开发框架 前端开发
在C#开发中使用第三方组件LambdaParser、DynamicExpresso、Z.Expressions,实现动态解析/求值字符串表达式
在C#开发中使用第三方组件LambdaParser、DynamicExpresso、Z.Expressions,实现动态解析/求值字符串表达式
|
5月前
|
自然语言处理 算法 搜索推荐
字符串相似度算法完全指南:编辑、令牌与序列三类算法的全面解析与深入分析
在自然语言处理领域,人们经常需要比较字符串,这些字符串可能是单词、句子、段落甚至是整个文档。如何快速判断两个单词或句子是否相似,或者相似度是好还是差。这类似于我们使用手机打错一个词,但手机会建议正确的词来修正它,那么这种如何判断字符串相似度呢?本文将详细介绍这个问题。
283 1
|
6月前
|
存储 算法 数据挖掘
深入解析力扣166题:分数到小数(模拟长除法与字符串操作详解及模拟面试问答)
深入解析力扣166题:分数到小数(模拟长除法与字符串操作详解及模拟面试问答)
|
7月前
|
SQL 存储 JSON
Hive 解析 JSON 字符串数据的实现方式
Hive 提供 `get_json_object` 函数解析 JSON 字符串,如 `{&quot;database&quot;:&quot;maxwell&quot;}`。`path` 参数使用 `$`、`.`、`[]` 和 `*` 来提取数据。示例中展示了如何解析复杂 JSON 并存储到表中。此外,Hive 3.0.0及以上版本内置 `JsonSerDe` 支持直接处理 JSON 文件,无需手动解析。创建表时指定 `JsonSerDe` 序列化器,并在 HDFS 上存放 JSON 文件,可以直接查询字段内容,方便快捷。
370 3
|
7月前
|
移动开发 iOS开发
非标准h5字符串的WKWebView展示前的解析与插入属性或标题头与解决WKWebView无法加载视频首帧问题
非标准h5字符串的WKWebView展示前的解析与插入属性或标题头与解决WKWebView无法加载视频首帧问题
58 1
|
6月前
|
XML 数据采集 自然语言处理
掌握Python字符串:全面解析与实战指南
掌握Python字符串:全面解析与实战指南
|
7月前
|
SQL 缓存 JavaScript
深入解析JavaScript中的模板字符串
深入解析JavaScript中的模板字符串
88 1