B3927 [GESP202312 四级] 小杨的字典

记录64

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1[110]={},s2[110]={},s,st;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s1[i]>>s2[i];
	cin>>s;
	for(int i=0;i<s.size();i++){
		st="";
		int num=i;
		bool f=0;
		if(s[i]<'a'||s[i]>'z') continue;
		while(s[i]>='a'&&s[i]<='z') st+=s[i++];
		//cout<<"st:"<<st<"  ";
		//printf("  num:%d  i:%d\n",num,i);
		for(int j=1;j<=n;j++){
			if(st==s1[j]){
				s.replace(num,st.size(),s2[j]);
				//cout<<s<<endl;
				i=num+s2[j].size()-1;
				f=1;
				//printf("下一次开始位置:%d\n",i);
				break;
			}
		}
		if(!f){
			s.replace(num,st.size(),"UNK");
			i=num+2;
		} 	
	}
	cout<<s;
	return 0;
}

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


突破口

小杨希望你写一个程序,帮助他根据这本字典翻译一段 A 语言文章。这段文章由标点符号 !()-[]{}\|;:'",./?<> 和一些 A 语言单词构成,每个单词之间必定由至少一个标点符号分割,你的程序需要把这段话中的所有 A 语言单词替换成它的 B 语言翻译。特别地,如果遇到不在字典中的单词,请使用大写 UNK 来替换它。

例如,小杨的字典中包含 2 个 A 语言单词 abcd,它们的 B 语言翻译分别为 adef,那么我们可以把 A 语言文章 abc.d.d.abc.abcd. 翻译成 B 语言文章 a.def.def.a.UNK. 其中,单词 abcd 不在词典内,因此我们需要使用 UNK 来替换它。


思路

  1. 将对应翻译的字符串分别存进两个字符串数组中
  2. 取出目标字符串S中的英文小写字符
  3. 对应着找翻译字符串
  4. 找到了就进行替换
  5. 没找到就替换为UNK

代码简析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1[110]={},s2[110]={},s,st;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s1[i]>>s2[i];
	cin>>s;
	for(int i=0;i<s.size();i++){
		st="";
		int num=i;
		bool f=0;
		if(s[i]<'a'||s[i]>'z') continue;
		...
	return 0;
}

string s1110={}(A 语言),s2110={}(B 语言),s(一段字符串S),st(S字符串切片);

int n(词典中的条目数);

for(int i=0;i<s.size();i++){} 👉 遍历整个字符串S

st=""; 👉 切片字符串初始化

int num=i; 👉存下开始的位置

bool f=0; 👉 默认没找到

if(si<'a'||si>'z') continue; 👉 不在小写字母内,直接跳过本次循环,找下一个开始位置

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s1[110]={},s2[110]={},s,st;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s1[i]>>s2[i];
	cin>>s;
	for(int i=0;i<s.size();i++){
		st="";
		int num=i;
		bool f=0;
		if(s[i]<'a'||s[i]>'z') continue;
		while(s[i]>='a'&&s[i]<='z') st+=s[i++];
		//cout<<"st:"<<st<"  ";
		//printf("  num:%d  i:%d\n",num,i);
		for(int j=1;j<=n;j++){
			if(st==s1[j]){
				s.replace(num,st.size(),s2[j]);
				//cout<<s<<endl;
				i=num+s2[j].size()-1;
				f=1;
				//printf("下一次开始位置:%d\n",i);
				break;
			}
		}
		if(!f){
			s.replace(num,st.size(),"UNK");
			i=num+2;
		} 	
	}
	cout<<s;
	return 0;
}

while(si>='a'&&si<='z') st+=si++; 👉 开始存储切片

for(int j=1;j<=n;j++){} 👉 遍历A语言字典

if(st==s1j){} 👉 在A语言字典找到了

s.replace(num,st.size(),s2j); 👉 用B语言替换它

注意 :关于replace函数,放在文章的补充部分

i=num+s2j.size()-1; 👉 下次循环的位置

例如:a.a.bcde.a.a 变成 a.a.qaq.a.a

用qaq替换bcde的话,b在下标4位置(0开始)

替换后下次是从q后面的.开始,下标为6,是第二个q的位置(开始位置+长度-1)

下次进入for循环i会自动+1变成7, 下次就会从.开始向后循环

f=1; break; 👉 替换语言找到了,跳出查找的for循环

if(!f){} 👉 没找到

s.replace(num,st.size(),"UNK"); 👉 替换为UNK

i=num+2; 👉 从k开始向后,跟上面的例子一样


补充

C++ string::replace() 函数完全指南


一、函数原型与功能

replace()std::string 的成员函数,用于替换字符串中的指定部分

核心原型

复制代码
string& replace(size_t pos, size_t len, const string& str);
  • pos :开始替换的位置(从0开始)

  • len :要替换的字符个数(若len过长,替换到末尾为止)

  • str :新插入的字符串

  • 返回值:修改后的字符串本身(支持链式调用)


二、四种重载形式

形式 语法 示例 说明
1. 替换为string replace(pos, len, str) s.replace(2, 3, "ABC") 最常用
2. 替换为C字符串 replace(pos, len, s2) s.replace(2, 3, "ABC", 2) 只取前2个字符
3. 替换为n个字符 replace(pos, len, n, c) s.replace(2, 3, 5, 'X') 替换为"XXXXX"
4. 迭代器范围 replace(it1, it2, str) s.replace(it, it+3, "ABC") 少用

三、使用示例

示例1:基础替换

cpp 复制代码
string s = "hello world";
s.replace(6, 5, "CSP-J");  
// 结果:s = "hello CSP-J"
// 解释:从位置6开始,删除5个字符("world"),插入"CSP-J"

示例2:替换长度超出原串

cpp 复制代码
string s = "hello";
s.replace(2, 10, "ABC");  
// 结果:s = "heABC"
// 解释:len=10超过剩余长度,只替换到末尾("llo") → "heABC"

示例3:替换为部分C字符串

cpp 复制代码
string s = "hello";
s.replace(2, 3, "ABCDEF", 2);  
// 结果:s = "heAB"
// 解释:只取"ABCDEF"的前2个字符

示例4:替换为n个相同字符

cpp 复制代码
string s = "hello";
s.replace(2, 2, 5, 'X');  
// 结果:s = "heXXXXXo"
// 解释:删除"ll",插入5个'X'

示例5:链式调用

cpp 复制代码
string s = "hello world";
s.replace(0, 5, "hi").replace(3, 5, "CSP");  
// 结果:s = "hi CSP"
// 解释:第一次替换"hello"→"hi",第二次替换"wor"→"CSP"

四、时间复杂度(竞赛核心)

时间复杂度O(n),其中n为字符串长度

为什么慢?

  • replace() 需要移动替换位置之后的所有字符

  • 每次调用可能触发内存重新分配(若新字符串更长)

性能测试

cpp 复制代码
string s = "a";
for (int i = 0; i < 1e5; i++) {
    s.replace(0, 1, "b");  // ❌ 极低效!O(n²) = 1e10
}
// 1e5次替换 ≈ 1e10次操作 → **必然TLE**

五、竞赛陷阱与替代方案

陷阱1:循环中频繁replace(必TLE)

cpp 复制代码
// ❌ 致命错误:O(n²)
for (int i = 0; i < n; i++) {
    s.replace(i, 1, "abc");  // 每次O(n),总O(n²)
}

// ✅ 正确方案:用ostringstream或预分配
ostringstream oss;
for (int i = 0; i < n; i++) {
    oss << "abc";  // O(n)总时间
}
string result = oss.str();

陷阱2:替换后迭代器失效

cpp 复制代码
// ❌ 错误:replace后迭代器失效
auto it = s.begin();
s.replace(it, it+3, "ABC");  // it已失效!

// ✅ 正确:重新获取迭代器
it = s.erase(it, it+3);  // erase返回新迭代器
it = s.insert(it, 'A');  // insert返回新迭代器

陷阱3:越界访问

cpp 复制代码
string s = "hello";
s.replace(10, 5, "abc");  // 位置10超出长度,未定义行为(可能崩溃)

六、竞赛替代方案(性能优先)

需求 replace() 推荐替代 原因
尾部拼接 s.replace(pos, 0, str) s += str O(n) vs O(1)(均摊)
删除子串 s.replace(pos, len, "") s.erase(pos, len) erase更语义化
多次拼接 循环replace ostringstream O(n) vs O(n²)
单字符替换 s.replace(pos, 1, "a") s[pos] = 'a' O(1) vs O(n)

竞赛铁律能用+=/erase/push_back的,绝不用replace


七、一句话总结

replace()用于一次性局部替换,O(n)复杂度,在循环中频繁使用会TLE;竞赛中优先用+=ostringstream或预分配空间,避免性能陷阱。

相关推荐
凡人叶枫1 小时前
Effective C++ 条款07:为多态基类声明 virtual 析构函数
linux·c语言·开发语言·c++
Black蜡笔小新1 小时前
自动化AI算法训练服务器DLTM训推一体工作站赋能多行业智能化升级
人工智能·算法·自动化
凡人叶枫1 小时前
Effective C++ 条款10:令 operator= 返回一个 reference to *this
java·linux·服务器·开发语言·c++·effective c++
王老师青少年编程1 小时前
2026年全国青少年信息素养大赛算法应用主题赛(C++赛项-复赛模拟卷6:文末附答案)
c++·答案·模拟卷·复赛·2026年·青少年信息素养大赛·算法应用主题赛
怪兽学LLM2 小时前
LeetCode 438 找到字符串中所有字母异位词(Python 固定滑动窗口+字符计数解法)
python·算法·leetcode
满怀冰雪2 小时前
第04篇-双指针算法-从有序数组到回文判断的高频解法
java·算法
CC数学建模2 小时前
2026年江西省研究生数学建模竞赛1题:空间数据分析中的过拟合识别完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模
leo__5202 小时前
MATLAB实现牧羊人算法
开发语言·算法·matlab
视觉小萌新2 小时前
C++利用libmicrohttpd制作交互网页端——C1
java·c++·交互
Gauss松鼠会2 小时前
【GaussDB】GaussDB SMP特性调优详解
java·服务器·前端·数据库·sql·算法·gaussdb