当前位置: 首页 > news >正文

UVa 12494 Distinct Substring

题目理解

给定一个字符串 SSS,我们需要计算其中所有不同的子串数量。这里的"不同"有一个特殊定义:如果两个子串可以通过循环移位变成相同,那么它们就被视为相同的子串。

循环移位的定义:对于长度为 nnn 的字符串 T=T1T2T3…TnT = T_1T_2T_3 \ldots T_nT=T1T2T3Tn,它的循环移位包括:

  • T1T2T3…TnT_1T_2T_3 \ldots T_nT1T2T3Tn(原始字符串)
  • T2T3…TnT1T_2T_3 \ldots T_nT_1T2T3TnT1
  • T3…TnT1T2T_3 \ldots T_nT_1T_2T3TnT1T2
  • …\ldots
  • TnT1T2…Tn−1T_nT_1T_2 \ldots T_{n-1}TnT1T2Tn1

示例说明:字符串 "aba""aba""aba""aab""aab""aab""baa""baa""baa" 的所有循环移位包括:

  • "aba""aba""aba""aba""aba""aba", "baa""baa""baa", "aab""aab""aab"
  • "aab""aab""aab""aab""aab""aab", "aba""aba""aba", "baa""baa""baa"
  • "baa""baa""baa""baa""baa""baa", "aab""aab""aab", "aba""aba""aba"

可以看到它们实际上共享相同的循环移位集合,因此被视为相同的子串。

解题思路

核心思想

为了解决这个问题,我们需要为每个子串找到一个标准表示,使得所有循环等价的子串都有相同的标准表示。这样,我们只需要统计不同标准表示的数量即可。

选择标准表示

最自然的选择是使用字典序最小的循环移位作为标准表示。例如:

  • 对于 "aba""aba""aba",循环移位有 "aba""aba""aba", "baa""baa""baa", "aab""aab""aab",其中 "aab""aab""aab" 是字典序最小的
  • 对于 "aab""aab""aab",循环移位有 "aab""aab""aab", "aba""aba""aba", "baa""baa""baa",最小也是 "aab""aab""aab"
  • 对于 "baa""baa""baa",循环移位有 "baa""baa""baa", "aab""aab""aab", "aba""aba""aba",最小也是 "aab""aab""aab"

这样,所有循环等价的子串都会映射到同一个标准表示。

算法步骤

  1. 枚举所有子串:对于长度为 nnn 的字符串,有 n(n+1)2\frac{n(n+1)}{2}2n(n+1) 个子串
  2. 计算最小循环表示:对每个子串,找到它的字典序最小的循环移位
  3. 去重计数:使用哈希集合存储所有最小循环表示,最后返回集合的大小

关键算法:最小循环表示

我们使用双指针算法O(L)O(L)O(L) 时间内找到长度为 LLL 的字符串的最小循环表示:

string minCyclicShift(const string& s) {int n = s.length();string t = s + s;  // 构造双倍字符串int i = 0, j = 1;while (i < n && j < n) {int k = 0;while (k < n && t[i + k] == t[j + k]) {k++;}if (k == n) {break;}if (t[i + k] > t[j + k]) {i += k + 1;} else {j += k + 1;}if (i == j) {j++;}}int pos = min(i, j);return t.substr(pos, n);
}

算法原理

  • 将原字符串复制一份接在后面,形成双倍字符串
  • 使用两个指针 ij 分别表示两个候选的起始位置
  • 通过比较 t[i + k]t[j + k] 来推进指针
  • 最终 min(i, j) 就是最小循环表示的起始位置

复杂度分析

  • 子串枚举O(n2)O(n^2)O(n2),其中 n≤200n \leq 200n200,最多 201002010020100 个子串
  • 最小循环表示计算:每个子串平均长度 O(n)O(n)O(n),计算复杂度 O(n)O(n)O(n)
  • 总复杂度O(n3)O(n^3)O(n3),对于 n=200n = 200n=200 大约是 8×1068 \times 10^68×106 次操作,完全可行

示例验证

S="abcba"S = "abcba"S="abcba" 为例:

子串最小循环表示是否新出现
“a”“a”
“b”“b”
“c”“c”
“ab”“ab”
“bc”“bc”
“cb”“bc”
“ba”“ab”
“abc”“abc”
“bcb”“bcb”
“cba”“abc”

最终得到 101010 个不同的标准表示,与题目示例一致。

参考代码

// Distinct Substring
// UVa ID: 12494
// Verdict: Accepted
// Submission Date: 2025-10-15
// UVa Run Time: 0.130s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>using namespace std;string minCyclicShift(const string& s) {int n = s.length();string t = s + s;int i = 0, j = 1;while (i < n && j < n) {int k = 0;while (k < n && t[i + k] == t[j + k]) {k++;}if (k == n) {break;}if (t[i + k] > t[j + k]) {i += k + 1;} else {j += k + 1;}if (i == j) {j++;}}int pos = min(i, j);return t.substr(pos, n);
}int main() {int T;cin >> T;while (T--) {string s;cin >> s;int n = s.length();unordered_set<string> seen;for (int i = 0; i < n; i++) {for (int j = i + 1; j <= n; j++) {string substr = s.substr(i, j - i);string canonical = minCyclicShift(substr);seen.insert(canonical);}}cout << seen.size() << endl;}return 0;
}

总结

本题的关键在于理解循环等价的概念,并通过最小循环表示将循环等价关系转化为字符串相等关系。使用双指针算法高效计算最小循环表示,结合哈希集合进行去重,最终得到正确答案。算法在给定的数据范围内具有很好的性能表现。

http://www.dtcms.com/a/487761.html

相关文章:

  • 【Linux】Linux进程间通信:命名管道(FIFO)的模拟实现重要知识点梳理
  • 做网站时怎么裁切存图最佳建站模板
  • 020网站建设如何保护我做的网站模板
  • Escrcpy 安卓手机投屏软件中文绿色版
  • 大模型实习
  • 如何做网站旅游产品分析网站建设与数据库管理
  • dw不用代码做网站w3school网页制作
  • 网站备案号如何查询密码室内设计需要什么学历
  • Git 用户名与邮箱配置指南
  • Spring 中使用的设计模式
  • SAP MM采购订单审批接口分享
  • 东莞网站制作哪家公司好价格低用英语怎么说
  • SpringBoot萌宠社交分享系统的设计与实现hfdwz(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 优秀画册设计网站电商加盟网站建设
  • 基于element-plus封装table组件
  • 长沙市网站建设公司网做影视网站需要境外
  • 全国响应式网站建设宜宾网站建设网站
  • 终极AI开发组合:Warp + Cursor + Claude Code 实战技巧分享
  • 响应式企业营销型网站多少钱企业策划书模板word
  • MetaShape(PhotoScan)——Camera calibration相机标定详解
  • 青岛好的网站制作推广wordpress速度确实差些
  • 网站seo步骤专业的传媒行业网站开发
  • 大模型-高频考点-每日一更【篇二】
  • 卡文迪许实验室:百年物理圣地的辉煌发展史
  • 服务器建设网站伪类网站
  • 网站建设论文模板小说网站建设目的
  • 模板建站有什么优势高大上网站设计
  • Windows Server 2012/2016 开启远程桌面
  • 网页设计课程主要内容网站关键词怎样优化
  • 深圳建设网站哪家强wordpress获取站点副标题