从小到大枚举子串长度,用集合检查所有同长度子串是否互不相同。
OJ: usaco
题目 ID: 964
难度:入门
标签:字符串枚举哈希表
日期: 2026-07-11 14:40
题意
给定一个长度为 N 的字符串 S,表示沿路邮箱颜色。
要求找到最小的 K,使得任意长度为 K 的连续子串都能唯一确定它在字符串中的位置。
换句话说,所有长度为 K 的子串必须互不相同。
思路
朴素判断
对固定的 K,可以枚举任意两个起点,直接比较两个长度为 K 的子串是否相等:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 14:40
* update_at: 2026-07-11 14:41
*/
// brute.cpp:小数据暴力解,用来帮助理解题意并辅助对拍。
#include <bits/stdc++.h>
using namespace std;
int n;
string s;
bool valid_len(int len) {
// 朴素检查:枚举两段长度为 len 的子串,看是否相同。
for (int i = 0; i + len <= n; i++) {
for (int j = i + 1; j + len <= n; j++) {
if (s.substr(i, len) == s.substr(j, len)) {
return false;
}
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> s;
for (int len = 1; len <= n; len++) {
if (valid_len(len)) {
cout << len << '\n';
return 0;
}
}
return 0;
}如果存在两个相同子串,说明这个 K 不可行。
用集合判重
正式代码从小到大枚举 K。
对每个 K,把所有长度为 K 的子串放进集合 seen:
- 如果插入前发现这个子串已经出现过,说明
K不可行; - 如果所有子串都没有重复,说明
K可行。
因为从小到大枚举,所以第一个可行的 K 就是答案。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-11 14:40
* update_at: 2026-07-11 14:41
*/
#include <bits/stdc++.h>
using namespace std;
int n;
string s;
bool has_duplicate(int len) {
set<string> seen;
for (int i = 0; i + len <= n; i++) {
string sub = s.substr(i, len);
if (seen.count(sub)) {
return true;
}
seen.insert(sub);
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> s;
for (int len = 1; len <= n; len++) {
if (!has_duplicate(len)) {
cout << len << '\n';
return 0;
}
}
return 0;
}复杂度
枚举长度、起点并构造子串,时间复杂度约为
集合中最多保存
总结
这题的关键是把“唯一确定位置”翻译成“同长度子串不能重复”。
然后从小到大枚举长度,做一次去重判断即可。