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 是一个非常有用的函数,可以方便地从字符串中提取子字符串。通过指定起始位置和长度,可以灵活地处理字符串的切片操作。

题目描述 有一个长度为 $n$ 的书架,每本书有一个高度 $h_i$。现在你可以进行以下两种操作: - 将一本书放在书架的最左边或最右边,花费为 $c_1$。 - 将一本高度为 $h_i$ 的书放在一本高度为 $h_j$ 的书的上面,花费为 $c_2$。 现在你需要将书架上的书按照高度从小到大排列,求最小花费。 输入格式 第一行包含三个整数 $n,c_1,c_2$。 第二行包含 $n$ 个整数 $h_i$。 输出格式 输出一个整数,表示最小花费。 数据范围 $1\leq n\leq 200,1\leq c_1,c_2\leq 10^9,1\leq h_i\leq 10^9$ 输入样例 5 1 2 3 1 4 2 5 输出样例 6 算法1 (动态规划) $O(n^2)$ 首先考虑一个朴素的 dp,设 $f_{i,j}$ 表示前 $i$ 本书已经排好序,第 $i+1$ 本书放在第 $j$ 个位置的最小花费。 状态转移方程为: $$ f_{i,j}=\min\{f_{i-1,k}+c_1\}+\begin{cases}&amp;\text{if }h_{i+1}>h_j\\c_2&amp;\text{otherwise}\end{cases} $$ 其中 $k$ 取遍 $1\sim i$,表示将第 $i+1$ 本书放在第 $k$ 个位置。 时间复杂度 $O(n^3)$ C++ 代码 算法2 (单调队列优化) $O(n^2)$ 考虑优化上述 dp,发现状态转移方程中的 $\min$ 操作可以用单调队列优化,具体来说,我们维护一个单调递增的队列 $q$,其中 $q_i$ 表示第 $i$ 个位置的最小花费,那么对于状态 $f_{i,j}$,我们只需要找到 $q$ 中第一个大于等于 $f_{i-1,k}+c_1$ 的位置 $p$,然后 $f_{i,j}=q_p+\begin{cases}&amp;\text{if }h_{i+1}>h_j\\c_2&amp;\text{otherwise}\end{cases}$。 时间复杂度 $O(n^2)$ C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值