用std::map统计长度≥2的子串频次需暴力枚举所有起始位置和长度,推荐用std::string_view作key以避免substr拷贝开销,注意原字符串生命周期;结果应过滤count>1后按长度降序、字典序升序排序。

用 std::map 统计所有长度 ≥ 2 的子串出现次数
直接暴力枚举所有子串是可行起点,但必须限制最小长度(比如 2),否则单字符会淹没结果,且不符合“重复子串”的常见语义。用 std::map<:string int></:string> 记录每个子串的出现频次,遍历所有起始位置和长度即可。
注意:对长字符串(如 > 1000 字符),O(n³) 时间可能明显卡顿;若只需找最长/最频繁的重复子串,后续可优化,但“所有”意味着必须穷举。
- 起始索引
i从0到s.length() - min_len - 子串长度
len从min_len到s.length() - i - 每次调用
s.substr(i, len)构造子串并递增计数 - 最后遍历 map,输出
count > 1的所有键值对
避免 substr 频繁拷贝导致性能陡降
std::string::substr 默认返回新字符串,对短子串影响小,但若原串大、子串多(比如 10k 子串),内存分配和拷贝开销会显著拖慢速度。实际项目中建议改用 std::string_view(C++17 起)作 key,前提是 map 的生命周期不超出原字符串作用域。
示例关键片段:
std::map<:string_view int> counts;
for (size_t i = 0; i <p>⚠️ 注意:<code>s</code> 必须保持有效(不能是临时 string 或已 move),否则 <code>string_view</code> 指向野内存。</p>
<h3>去重与排序:按长度降序、再按字典序升序输出</h3>
<p>原始 map 按字典序排列 key,但用户通常更关心“最长重复子串”或“高频短子串”。建议把结果导出到 vector 后自定义排序:</p>
<ul>
<li>先过滤出 <code>count > 1</code> 的项</li>
<li>用 lambda 排序:<code>[&](const auto& a, const auto& b) { return a.first.size() != b.first.size() ? a.first.size() > b.first.size() : a.first </code>
</li>
<li>避免在 map 内部强行重定义比较逻辑——易出错且无必要</li>
</ul>
<h3>边界情况:空串、单字符、全相同字符</h3>
<p>这些输入容易让代码产出大量无意义结果或崩溃:</p>
<ul>
<li>
<code>""</code> → 无子串,循环不执行,安全</li>
<li>
<code>"a"</code> → 最小长度设为 <code>2</code> 时,内层循环不触发,返回空结果</li>
<li>
<code>"aaaa"</code> → 子串 <code>"aa"</code> 出现 3 次,<code>"aaa"</code> 出现 2 次,<code>"aaaa"</code> 出现 1 次;注意不要漏掉重叠匹配(如 <code>"aa"</code> 在位置 0、1、2 都存在)</li>
<li>若需排除重叠匹配(即要求子串不重叠),算法需额外维护已覆盖区间,复杂度上升,不属于“所有重复子串”的默认含义</li>
</ul>
<p>真正难处理的是超长字符串加宽松长度下限——这时候得换后缀数组或后缀自动机,但那是另一类问题了。</p></:string_view>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











