P5832 [USACO19DEC] Where Am I? B

记录11

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    string s;
    cin>>n;
    cin>>s;
    int len=1; 
    while (len<=n) {
        bool flag = true;
        for (int i=0; i+len <= n; i++) {
            string s1=s.substr(i, len);
            for (int j=i+1; j+len<=n;j++) {
                string s2 =s.substr(j, len);
                if (s1==s2) {
                    flag=false;
                    break;
                }
            }
            if (!flag) break;
        }
        if (flag) break; 
        len++; 
    }
    cout<<len<<endl;
    return 0;
}


突破点

“每个邮箱的颜色用 A..Z 之间的一个字母来指定”👉字符串

“使得他查看任意连续 K 个邮箱序列,他都可以唯一确定这一序列在道路上的位置”👉字符串的子串不重复

“输出一行,包含一个整数,为可以解决 Farmer John 的问题的最小 K 值。”👉最小的不重复子串

思路

  1. 数据不大,又要遍历所有情况,直接暴力枚举
  2. 对字符串要进行切割子串操作

对字符串操作的常用的函数,我会放在文章的补充部分来讲解

代码简析

int len=1; 刚开始的时候,把字符串切割为长度为1子串

while (len<=n) {
    bool flag = true;
    ...
    if (flag) break; 
    len++; 
    }

while循环的代表每一次子串长度的比较,len<=n子串长度不超过字符串长度

bool flag = true;默认找到了符合条件的子串长度

if (flag) break; 找到了就退出while循环

len++;一次while循环结束,查找的子串长度加一

while (len<=n) {
        bool flag = true;
        for (int i=0; i+len <= n; i++) {
            string s1=s.substr(i, len);
            ....
            ....
            if (!flag) break;
        }
    }

i=0,不同长度的子串,每一次都从字符串的下标为0开始取

string s1=s.substr(i, len);切割子串为s1,然后开始偏移

if (!flag) break;没有找到就跳出外层for循环,这也说明了内层for循环遇到不符合情况就跳出

for (int j=i+1; j+len<=n;j++) {
                string s2 =s.substr(j, len);
                if (s1==s2) {
                    flag=false;
                    break;
                }
            }

内层for循环从第二个子串开始向后慢慢比较,找到不满足条件的情况就修改flag,跳出内层for循环

补充

在C++中,std::string 类提供了 substr 函数,用于从字符串中提取子字符串。这个函数非常有用,可以方便地处理字符串的切片操作。

函数原型

substr 函数的原型如下:

std::string substr(size_t pos = 0, size_t len = npos) const;

参数

  • pos:子字符串的起始位置,默认值为0。

  • len:子字符串的长度,默认值为 std::string::npos,表示从 pos 开始到字符串末尾。

返回值

  • 返回从位置 pos 开始,长度为 len 的子字符串。

异常

  • 如果 pos 超出字符串的范围,会抛出 std::out_of_range 异常。

  • 如果 len 超出字符串的范围,substr 会返回从 pos 开始到字符串末尾的子字符串。

示例

示例1:提取子字符串
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, World!";
    string sub = str.substr(7, 5); // 从位置7开始,提取长度为5的子字符串
    cout << sub << endl; // 输出:World
    return 0;
}
示例2:省略长度参数
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, World!";
    string sub = str.substr(7); // 从位置7开始,提取到字符串末尾
    cout << sub << endl; // 输出:World!
    return 0;
}
示例3:处理越界情况
#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello, World!";
    try {
        string sub = str.substr(20, 5); // 位置20超出字符串范围
        cout << sub << endl;
    } catch (const std::out_of_range& e) {
        cout << "Error: " << e.what() << endl; // 输出:Error: basic_string::substr: __pos (which is 20) > this->size() (which is 13)
    }
    return 0;
}

注意事项

  1. 边界检查

    • 在调用 substr 时,确保 pos 不超出字符串的范围,否则会抛出 std::out_of_range 异常。

    • 如果 len 超出字符串的范围,substr 会返回从 pos 开始到字符串末尾的子字符串。

  2. 默认参数

    • pos 的默认值为0,表示从字符串的开头开始。

    • len 的默认值为 std::string::npos,表示从 pos 开始到字符串末尾。

总结

std::string::substr 是一个非常有用的函数,可以方便地从字符串中提取子字符串。通过指定起始位置和长度,可以灵活地处理字符串的切片操作。

### 题目解析 USACO 2019年12月比赛中的问题 **"Where Am I?"** 是一道考察字符串处理与唯一性识别的编程题,属于青铜组(Bronze)的经典题目。该题要求选手找出最小的长度 $ k $,使得从每个位置开始、长度为 $ k $ 的子串都是唯一的。 具体来说,输入是一个整数 $ n $ 和一个字符串 $ s $,要求输出最小的 $ k $ 值,使得对于字符串中所有长度为 $ k $ 的子串,没有两个是完全相同的。这道题可以通过暴力枚举子串长度并逐一验证来解决。 以下是一个符合要求的 C++ 实现: ```cpp #include <bits/stdc++.h> using namespace std; int n; string s; int main() { cin >> n >> s; for (int i = 1; i <= s.length(); i++) { int mark = 0; for (int j = 0; j < s.length(); j++) { string s1 = s.substr(j, i); for (int k = j + 1; k < s.length(); k++) { string s2 = s.substr(k, i); if (s1 == s2) { mark = 1; break; } } if (mark == 1) break; } if (mark == 0) { cout << i << endl; return 0; } } return 0; } ``` 上述代码首先遍历可能的子串长度 $ i $,然后对每个长度检查是否所有长度为 $ i $ 的子串都唯一。一旦发现某个长度满足条件,立即输出并终止程序[^1]。 ### 解法分析 - **时间复杂度**:由于需要嵌套遍历所有可能的子串组合,算法的时间复杂度约为 $ O(n^3) $,在 $ n $ 较小时可以接受。 - **优化思路**:如果数据规模更大,可以考虑使用哈希集合(`unordered_set`)来存储已经出现过的子串,从而将查找操作优化到 $ O(1) $ 时间复杂度。 - **空间复杂度**:使用哈希表存储子串时,空间复杂度为 $ O(n^2) $,因为最多有 $ n $ 个不同长度的子串。 ### 示例优化代码 以下是使用 `unordered_set` 进行优化后的实现: ```cpp #include <bits/stdc++.h> using namespace std; int n; string s; int main() { cin >> n >> s; for (int i = 1; i <= n; i++) { unordered_set<string> seen; bool unique = true; for (int j = 0; j + i <= n; j++) { string sub = s.substr(j, i); if (seen.count(sub)) { unique = false; break; } seen.insert(sub); } if (unique) { cout << i << endl; return 0; } } return 0; } ``` 该版本通过哈希集合记录已出现的子串,避免了重复比较,提升了运行效率。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值