P2957 [USACO09OCT] Barn Echoes G

记录67

#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1,s2,st1,st2;
	int ans1=0,ans2=0;
	cin>>s1>>s2;
	int len1=s1.size();
	int len2=s2.size();
	int len=min(len1,len2);
	for(int i=0;i<len;i++){
		if(s1[i]==s2[len2-1]){
			st1=s1.substr(0,i+1);//i+1是长度
			st2=s2.substr(len2-i-1,i+1);
			if(st1==st2) ans1=i+1;
		}
	}
	for(int i=0;i<len;i++){
		if(s2[i]==s1[len1-1]){
			st2=s2.substr(0,i+1);
			st1=s1.substr(len1-i-1,i+1);
			if(st1==st2) ans2=i+1;
		}
	}
	cout<<max(ans1,ans2);
	return 0;
}

题目传送门https://www.luogu.com.cn/problem/P2957


突破口

我们通过一个例子来理解题目。考虑下面的两个哞声:

moyooyoxyzooo
yzoooqyasdfljkamo

第一个串的最后的部分 yzooo 跟第二个串的第一部分重复。第二个串的最后的部分 mo 跟第一个串的第一部分重复。所以 yzooo 跟 mo 都是这 2 个串的重复部分。其中,yzooo 比较长,所以最长的重复部分的长度就是 5。

两个声音的重复部分有多长。


思路

  1. 字符串1从头开始找,字符串2从后开始找,相同的部分最大值用答案1保存
  2. 字符串2从头开始找,字符串1从后开始找,相同的部分最大值用答案2保存
  3. 取两个答案中大的那个值

代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1,s2,st1,st2;
	int ans1=0,ans2=0;
	cin>>s1>>s2;
	int len1=s1.size();
	int len2=s2.size();
	int len=min(len1,len2);
	for(int i=0;i<len;i++){
		if(s1[i]==s2[len2-1]){
			st1=s1.substr(0,i+1);//i+1是长度
			st2=s2.substr(len2-i-1,i+1);
			if(st1==st2) ans1=i+1;
		}
	}
	for(int i=0;i<len;i++){
		if(s2[i]==s1[len1-1]){
			st2=s2.substr(0,i+1);
			st1=s1.substr(len1-i-1,i+1);
			if(st1==st2) ans2=i+1;
		}
	}
	cout<<max(ans1,ans2);
	return 0;
}

string s1(字符串1),s2(字符串2),st1(切片1),st2(切片2);

 int ans1=0(答案1),ans2=0(答案2);  👉  没有相同的长度就是0

int len1=s1.size();  👉 字符串1的长度

int len2=s2.size();  👉  字符串2的长度

int len=min(len1,len2);  👉  取最短的那一个作为循环的次数

for(int i=0;i<len;i++){}  👉  字符串开始遍历字符

if(s1[i]==s2[len2-1]){}  👉  找到跟字符串尾巴一样的地方

st1=s1.substr(0,i+1);  👉  字符串1从下标0开始切,得到切片1,i+1是长度

st2=s2.substr(len2-i-1,i+1);  👉  字符串2从尾巴往前数i+1开始切出来切片2

if(st1==st2) ans1=i+1;  👉  相同得到答案1

注意:切的长度是越来越长的,所以不用比较,有大的值自动更新

后面的for循环同理,只是前后的角色互换了

cout<<max(ans1,ans2);  👉  输出最长的那一个


补充

CSP-J 比赛中高频用到的字符串处理方法

一、基础存储与遍历(入门级必掌握)

这是字符串处理的前提,CSP-J 中优先推荐使用 C++ 的string类(比字符数组char[]更易用、不易出错)。

1. 存储方式
类型用法示例优点注意点
string 类string s = "abc123";自带成员函数,支持直接拼接下标从 0 开始
字符数组char s[100] = "abc123";兼容 C 语言,节省空间需手动管理长度,易越界
2. 遍历方式(CSP-J 高频)

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "CSP-J2025";
    
    // 方式1:下标遍历(最常用)
    for (int i = 0; i < s.size(); i++) {
        cout << s[i] << " "; // 输出:C S P - J 2 0 2 5
    }
    cout << endl;
    
    // 方式2:范围for(C++11,比赛可用)
    for (char c : s) {
        cout << c << " "; // 效果同上
    }
    
    return 0;
}

二、string 类核心成员函数(CSP-J 高频)

string类的内置函数是处理字符串的核心,以下是考试中最常考的函数:

函数功能说明示例(s = "abc123abc")
size()/length()返回字符串长度(无符号整数)s.size() → 9
empty()判断字符串是否为空(空返回 true)s.empty() → false
s + t/append()字符串拼接"abc" + "123" → "abc123"
substr(pos, len)截取子串:从 pos 开始,长度为 len(len 省略则到末尾)s.substr(3,3) → "123";s.substr(6) → "abc"
find(str/char)查找子串 / 字符首次出现的下标,找不到返回string::nposs.find("123") → 3;s.find('x') → npos
rfind(str/char)查找子串 / 字符最后一次出现的下标s.rfind("abc") → 6
erase(pos, len)删除子串:从 pos 开始,长度为 lens.erase(3,3) → "abcabc"

示例:子串查找与判断(CSP-J 常考题)

// 判断字符串s中是否包含子串"J2025"
string s = "CSP-J2025";
if (s.find("J2025") != string::npos) {
    cout << "包含该子串" << endl; // 输出此句
} else {
    cout << "不包含" << endl;
}

三、字符级判断与转换(必考)

处理单个字符的类型判断、大小写转换,需包含<cctype>头文件。

函数功能说明示例
isdigit(c)判断字符是否为数字isdigit('5') → true
isalpha(c)判断字符是否为字母isalpha('A') → true
islower(c)判断字符是否为小写字母islower('b') → true
isupper(c)判断字符是否为大写字母isupper('C') → true
tolower(c)转小写(非字母不变)tolower('A') → 'a'
toupper(c)转大写(非字母不变)toupper('b') → 'B'

示例:统计字符串中的数字和字母数量

#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string s = "CSP-J2025!@#";
    int num_cnt = 0, alpha_cnt = 0;
    for (char c : s) {
        if (isdigit(c)) num_cnt++;
        else if (isalpha(c)) alpha_cnt++;
    }
    cout << "数字:" << num_cnt << endl; // 4
    cout << "字母:" << alpha_cnt << endl; // 4
    return 0;
}

四、字符串与数值的转换(高频)

CSP-J 常考 “字符串转数字”“数字转字符串”,需包含<string>头文件,支持 C++11(比赛环境兼容)。

转换方向函数 / 方法适用场景注意点
字符串→整数stoi(s)转换 int 范围的整数(含负数)超出 int 范围会抛异常
字符串→长整数stoll(s)转换 long long 范围的大数支持负数,适合大数场景
数值→字符串to_string(num)支持 int/long long/double万能转换,无需手动处理格式

示例:转换与计算

#include <iostream>
#include <string>
using namespace std;

int main() {
    // 字符串转数字(支持负数)
    string s1 = "-12345", s2 = "9876543210123";
    int n1 = stoi(s1); // -12345
    long long n2 = stoll(s2); // 9876543210123
    
    // 数字转字符串
    int a = 678;
    long long b = -987654321;
    string s3 = to_string(a); // "678"
    string s4 = to_string(b); // "-987654321"
    
    cout << n1 + a << endl; // 666
    cout << s3 + s4 << endl; // "678-987654321"
    return 0;
}

五、CSP-J 典型场景处理技巧

1. 回文判断(高频模拟题)

核心:双指针法,左右两端向中间对比字符。

bool isPalindrome(string s) {
    int l = 0, r = s.size() - 1;
    while (l < r) {
        if (s[l] != s[r]) return false;
        l++;
        r--;
    }
    return true;
}
// 调用:isPalindrome("abba") → true;isPalindrome("abc") → false
2. 字符串分割(按分隔符,如空格、逗号)

CSP-J 常考 “按空格分割字符串为多个单词”,模拟遍历即可:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

vector<string> split(string s, char sep = ' ') {
    vector<string> res;
    string temp;
    for (char c : s) {
        if (c == sep) {
            if (!temp.empty()) { // 避免空串(多个分隔符)
                res.push_back(temp);
                temp.clear();
            }
        } else {
            temp += c;
        }
    }
    if (!temp.empty()) res.push_back(temp); // 处理最后一个单词
    return res;
}

int main() {
    string s = "CSP-J 2025 入门级";
    vector<string> words = split(s);
    for (string word : words) {
        cout << word << endl; // 输出:CSP-J、2025、入门级
    }
    return 0;
}
3. 去除字符串中的空格 / 无用字符

核心:遍历筛选符合条件的字符,重新拼接。

string removeSpace(string s) {
    string res;
    for (char c : s) {
        if (c != ' ') res += c; // 保留非空格字符
    }
    return res;
}
// 调用:removeSpace("a b c 1 2 3") → "abc123"

六、避坑指南(CSP-J 易失分点)

  1. 下标越界string的下标从 0 开始,s.size()返回无符号整数,避免写i <= s.size()-1(当 s 为空时,s.size()-1会变成极大值),建议写i < s.size()
  2. 空字符串处理:操作前先用empty()判断,避免对空串调用substr()find()等函数。
  3. 字符数组与 string 转换:若需用printf输出string,需用s.c_str()转换为字符数组:printf("%s\n", s.c_str())

总结

  1. CSP-J 字符串处理优先使用string类,核心掌握遍历substr/find字符判断 / 转换数值转换四大类操作;
  2. 典型场景(回文、分割、去空格)用 “双指针”“遍历筛选”“模拟拼接” 即可解决,无需复杂算法;
  3. 避坑重点是下标越界空串处理,这是入门级比赛最易失分的细节。
### 题目背景 由于FJ的田地中有树木和其他障碍物,FJ让犁在许多不同的矩形区域耕地,这些矩形区域可能会重叠。他想知道在按照各种耕地指令(每个指令通过给出矩形的左下角和右上角的x、y坐标来描述)对犁进行编程后,田地里实际被耕过的方格数量是多少 [^2]。 ### 解题思路 1. **输入处理**:读取田地的大小 `x` 和 `y`,以及耕地指令的数量 `t`。 2. **标记耕地区域**:对于每个耕地指令,读取矩形的左下角坐标 `(x1, y1)` 和右上角坐标 `(x2, y2)`,将该矩形区域内的方格标记为已耕。 3. **统计耕地数量**:遍历整个田地,统计被标记为已耕的方格数量。 4. **输出结果**:输出实际被耕过的方格数量。 ### 代码实现 #### C++ 实现 1 ```cpp #include<bits/stdc++.h> using namespace std; int a[250][250],x,y,t,cnt; int main() { cin >> x >> y >> t; int x1,y1,x2,y2; while(t--) { cin >> x1 >> y1 >> x2 >> y2; for(int i=x1;i<=x2;i++) for(int j=y1;j<=y2;j++) a[i][j]=1; } for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) if(a[i][j]==1)cnt++; cout << cnt << endl; return 0; } ``` #### C++ 实现 2 ```cpp #include<iostream> #include<cstring> using namespace std; int space[250][250]; int main() { ios::sync_with_stdio(false); int x, y, i, x1, y1, x2, y2, count = 0; cin >> x >> y >> i; memset(space, 0, sizeof(space)); for(int a = 0; a < i; a++) { cin >> x1 >> y1 >> x2 >> y2; for(int b = x1; b <= x2; b++) for(int c = y1; c <= y2; c++) space[b][c]++; } for(int m = 1; m <= x; m++) for(int n = 1; n <= y; n++) if(space[m][n] > 0) count++; cout << count; return 0; } ``` #### C 语言实现 ```c #include<stdio.h> #include<ctype.h> #include<string.h> #include<math.h> #include<stdlib.h> int main() { int x, y, num, x1, y1, x2, y2, cnt = 0; scanf("%d %d %d", &x, &y, &num); int sign[x][y], i, j; for ( i = 0; i < x; i++) for ( j = 0; j < y; j++) sign[i][j] = 0; while(num) { scanf("%d %d %d %d", &x1, &y1, &x2, &y2); for ( i = x1 - 1; i <= x2 - 1; i++) for ( j = y1 - 1; j <= y2 - 1; j++) sign[i][j] = 1; num--; } for (i = 0; i < x; i++) for ( j = 0; j < y; j++) { if ( sign[i][j] == 1) cnt++; } printf("%d", cnt); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值