L1-064 估值一亿的ai核心代码 (分数20)字符串处理
•无论用户说什么,首先把对方说的话在一行中原样打印出来;
•消除原文中多余空格:把相邻单词间的多个空格换成 1 个空格,把行首尾的空格全部删掉,把标点符号前面的空格删掉;
•把原文中所有大写英文字母变成小写,除了 I;•把原文中所有独立的 can you、could you 对应地换成 I can、I could—— 这里“独立”是指被空格或标点符号分隔开的单词;
•把原文中所有独立的 I 和 me 换成 you;
•把原文中所有的问号 ? 换成惊叹号 !;
•在一行中输出替换后的句子作为 AI 的回答。
现在请你模拟手搓一个能满足这些功能代码来模拟ai。
我来模拟ai?!真的假的(👿)
输入格式
输入首先在第一行给出不超过 10 的正整数 N,随后 N 行,每行给出一句不超过 1000 个字符的、以回车结尾的用户的对话,对话为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。
输出格式
按题面要求输出,每个 AI 的回答前要加上 AI: 和一个空格。
解题思路
算法一:朴素做法
我先要介绍一种处理字符串中单词之间多个空格的方法
我们可以像之前L1-049那样,用一个last来记录上一个字符是啥状态 💪\
比如,bool last_is_space= true; 再利用isalnum(char)函数完成对输入字符串的整理,记整理后的字符串为clean
由于这里需要先输出原句,所以肯定需要使用getline,不能用cin自动跳过不可见字符
遍历字符串的过程中,我们需要分三种情况讨论:1. 单词(包含数字和字母) 2. 空格 3. 标点符号 ;
●如果该 字符 是 单词 , 就直接放进clean字符串;
●如果是空格,检查last_is_space,如果是false,就放进clean,并改变状态
●如果是标点符号,要检查last_is_space和clean.size() , 因为要去掉标点符号前的空格,但是标点符号可能出现在字符串第一个,而刚开始
last_is_space 的状态就是true,所以还要检查clean.size(),任何访问容器元素的操作都要判断是否为空;或者可以检查clean.size() && clean.back()
如果有空格就 pop.back() 后者更符直觉,前者符合前面的代码设计
注意: 常见的容器 如vector 和 string 都有 front()和 back() ,之前我也不知道这个o(╥﹏╥)o
然后把整个字符串clean之后,就是对单词大小写(为了方便可以在clean的过程中转换为小写),特定词的替换,问号进行处理。
ac代码✅️
#include<iostream> |
|
#include<string> |
|
#include<vector> |
|
#include<cctype> |
|
using namespace std; |
|
bool isSymbol(char c) |
|
{ |
|
return !isalnum(c); |
|
} |
|
int main() |
|
{ |
|
int n; |
|
cin>>n; |
|
cin.ignore(); |
|
while(n--) |
|
{ |
|
string s; |
|
getline(cin,s); |
|
cout<<s<<endl; |
|
string clean; |
|
bool lastIsSpace = true; |
|
for(int i = 0 ; i < s.size() ; i++) |
|
{ |
|
char c = s[i]; |
|
if(c == ' ') |
|
{ |
|
if(!lastIsSpace) |
|
{ |
|
clean += ' '; |
|
lastIsSpace = true; |
|
} |
|
} |
|
else if( isSymbol(c) ) |
|
{ |
|
//这里提个问题,为啥 “只” 要检查 “末尾” 是否是空格 , 前面不可能有空格吗? |
|
if(lastIsSpace && clean.size()) clean.pop_back(); |
|
if(c == '?') c = '!'; |
|
clean += c; |
|
lastIsSpace = false; |
|
} |
|
else |
|
{ |
|
if(c != 'I') c = tolower(c); |
|
clean += c; |
|
lastIsSpace = false; |
|
} |
|
} |
|
if(clean.size() > 0 && clean.back() == ' ') clean.pop_back(); |
|
vector<string> tokens; |
|
string temp = ""; |
|
for(int i = 0 ; i < clean.size() ; i++) |
|
{ |
|
if(isalnum(clean[i])) temp += clean[i]; |
|
else |
|
{ |
|
if(temp != "") |
|
{ |
|
//这里的作用是把每个单词放进tokens,便于单词替换 |
|
//遇到空格或者标点符号说明放完一个单词了 |
|
//接下来要清空temp便于下一个单词的放入 |
|
tokens.push_back(temp); |
|
temp = ""; |
|
} |
|
//char要转换为string才能放入 |
|
string sym(1,clean[i]); |
|
tokens.push_back(sym); |
|
} |
|
} |
|
//clean的行末可能有空格,可能没有,如果没有,会有一个temp没被加 |
|
//需要在循坏结束后特判一下 |
|
if(temp != "") tokens.push_back(temp); |
|
for(int i = 0 ; i < tokens.size() ; i++) |
|
{ |
|
string &now = tokens[i]; |
|
if(now == "can" && i + 2 < tokens.size() && tokens[i+1] == " " && tokens[i+2] == "you") |
|
{ |
|
tokens[i] = "I"; |
|
tokens[i+2] = "can"; |
|
i+=2; |
|
} |
|
else if(now == "could" && i + 2 < tokens.size() && tokens[i+1] == " " && tokens[i+2] == "you") |
|
{ |
|
tokens[i] = "I"; |
|
tokens[i+2] = "could"; |
|
i += 2; |
|
} |
|
else if(now == "I") tokens[i] = "you"; |
|
else if(now == "me") tokens[i] = "you"; |
|
} |
|
cout<<"AI: "; |
|
for(const string &t : tokens) cout<<t; |
|
cout<<endl; |
|
} |
|
return 0; |
|
} |
😄还没完,还有一种牛逼轰轰的写法
- (后续我发现并没有)用到了s.find_first_not_of(" ") 和 s.find_last_not_of(" ") 分别可以找到字符串第一个不为空格的下标,和最后一个不为空格的下标,完美地控制了主体部分的范围。然后遍历主体,如果s[i] 和 s[i+1] 都是空格,就可以continue,保证只加入了一个空格
- 其实这里可以在clean的过程中 改变大小写和问号换感叹号等等;
- 只需要考虑单词替换的问题。这里用到了正则表达式 。
这里只介绍regex的replace函数
- 单词替换
#include<iostream> |
|
#include<regex> |
|
using namespace std; |
|
int main() |
|
{ |
|
string s = "I love you,can you love me?"; |
|
s = regex_replace(s , regex("\\bcan you\\b"), "I can"); |
|
cout<<s<<endl; |
|
return 0; |
|
} |
- clean过程
s = regex_replace(s, regex("^ +| +$"), ""); // 删去首尾空格 |
|
s = regex_replace(s, regex(" +"), " "); // 多空格压成单空格 |
|
s = regex_replace(s, regex(" +([^a-zA-Z0-9 ])"), "$1"); // 删去标点前的空格 (匹配非字母数字空格) |
解释说明
- \b表示边界,也可以说是与其他词的边界,在上述演示中,\b严格限制了替换的内容,它与find不同的是,如果字符串中有oldcan youschool,find会找到
can you并替换,而regex_replace找不到,它只能找到独立的can you。
- regex("^ +| +$")在该函数中起到了 搜索的作用 , ^表示从开头开始匹配 , 接下来的 “ +” 表示匹配开头的多个空格
"|"表示 或 , 即左右两种匹配搜索方式都可以 ,$$表示 结尾 , “ +$”表示匹配结尾前面的多个空格,实现了去除字符串
开头或结尾的多余空格
- regex(" +([^a-zA-Z0-9 ])")表示匹配([^a-zA-Z0-9 ])前面的多个空格,[]表示指定一个字符集,其内容为^a-zA-Z0-9 (这里有个空格请注意),^
为反的意思,a-zA-Z0-9 就是字符串中不是字母也不是数字还不是空格的字符,在题目限制下就是标点符号(逗号,感叹号等等),将标点符号和前面的空格
全部换成标点符号,"$1"表示[]匹配到的内容,这里就是标点符号。
ac代码✅️✅️
#include <iostream> |
|
#include <string> |
|
#include <regex> |
|
#include <cctype> |
|
using namespace std; |
|
void solve() |
|
{ |
|
string s; |
|
getline(cin, s); |
|
cout << s << endl; // 第一步:原样输出 |
|
// 1. 朴素版:大小写转换与问号替换 (这一步必须手动,最快最稳) |
|
for (int i = 0; i < s.size(); i++) |
|
{ |
|
if (s[i] >= 'A' && s[i] <= 'Z' && s[i] != 'I') |
|
{ |
|
s[i] = tolower(s[i]); |
|
} |
|
else if (s[i] == '?') |
|
{ |
|
s[i] = '!'; |
|
} |
|
} |
|
// 2. 正则版:清理空格 (顺序千万不能乱) |
|
s = regex_replace(s, regex("^ +| +$"), ""); // 删去首尾空格 |
|
s = regex_replace(s, regex(" +"), " "); // 多空格压成单空格 |
|
s = regex_replace(s, regex(" +([^a-zA-Z0-9 ])"), "$1"); // 删去标点前的空格 (匹配非字母数字空格) |
|
// 3. 正则版:单词替换 (利用大写 A 作为绝妙占位符) |
|
s = regex_replace(s, regex("\\bcan you\\b"), "A can"); |
|
s = regex_replace(s, regex("\\bcould you\\b"), "A could"); |
|
s = regex_replace(s, regex("\\bI\\b"), "you"); |
|
s = regex_replace(s, regex("\\bme\\b"), "you"); |
|
s = regex_replace(s, regex("\\bA\\b"), "I"); // 最后把 A 变回 I |
|
cout << "AI: " << s << endl; |
|
} |
|
int main() |
|
{ |
|
int n; |
|
if (cin >> n) |
|
{ |
|
cin.ignore(); |
|
while (n--) |
|
{ |
|
solve(); |
|
} |
|
} |
|
return 0; |
|
} |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)