『题解』CodeForces CF21A Jabber ID
题目大意
给定一个地址,由 <username>
,<hostname>
和 <resource>
三个字符串组成。地址结构:<username>@<hostname>/<resource>
。其中 /<resource>
可省略。
- 对于
<username>
,约束如下: - 对于每个字符,只允许大写,小写字母,数字和下划线。
- 其长度应在 \(1\) 到 \(16\) 之间。
- 对于
<hostname>
,约束如下: - 可用
.
分隔成多段,每段约束同<username>
。 - 总长度应在 \(1\) 到 \(32\) 之间。
- 对于
<resource>
,约束同<username>
思路
众所周知,CF题没有一道不坑的。
我们直接输入整个字符串 \(s\),并拆成三个字符段,每个字符串用一个新的 \(string\) 来存。分别命名为 \(u,h,r\)。
拆分字段
如何拆分呢?
我们先定位符号 @
的位置和 /
的位置,用 \(string\) 自带的 \(find\) 函数。\(find\) 函数返回字符在字符串中的下标,如果没找到,返回 \(string::npos\) 这个值。因此可以很方便地定位符号。
再使用 \(string\) 的 \(substr\) 函数截取字符串,传入两个参数:起始下标和截取字符个数。
<username>
字段要从 \(0\) 开始截取到符号@
的位置,所以要截取@
符号下标个字符,所以这个字段就是 \(s.substr(0,s.find('@'))\)。<hostname>
字段要从 \(s.find('@')+1\) 处开始截取,我们假设有resource
字段,这个字段开始下标为 \(s.find('/')+1\),减去 \(s.find('@')+1\) 就是要截取的字符个数了,是不是?那么这个字段就是 \(s.substr(s.find('@')+1,s.find('/')-(find('@')+1))\) 了。 那如果没有resource
字段呢?那 \(s.find('/')\) 就是 \(string::npos\) 了啊,是不是要加个判断?其实不需要!\(string::npos\) 的取值是一个较大的数,减去 \(find('@')+1\) 后依然很大,会超出范围。但是,万能的 \(STL\) 帮了我们,\(substr\) 函数如果发现你要截取的字符超出范围,那么它自动截取到最后一个,刚好就是<hostname>
字段了!<resource>
字段很简单,先判断是否有这个字段,也就是 \(s.find('/')!=string::npos\) 时说明有这个字段。起始下标为 \(s.find('/')+1\),截取字符个数自然是到末尾了,也就是 \(s.size()-(s.find('/')+1)\) 了。这个字段就是 \(s.substr(s.find('/')+1,s.size()-(s.find('/')+1))\)。
为了节省时间,使用 \(sz\) 存储 \(s.size()\),用 \(fd1\) 存储 \(s.find('@')\),用 \(fd2\) 存储 \(s.find('/')\)。
拆分代码如下:
u=s.substr(0,fd1); // username 串
h=s.substr(fd1+1,fd2-(fd1+1)); // hostname 串
if(fd2!=string::npos){ // 如果有 resoure,就取。r 默认为空
r=s.substr(fd2+1,sz-fd2);
}
判断不规范情况
接下来,我们定义三个函数,分别判断三个字符串是否合法,如果不合法,直接在函数内输出并结束程序即可,函数都调用完后如果一直没退出程序,说明一切正常,输出 YES
即可。
这三个字段分别有什么限制呢?也就是函数中如何判断呢?
-
<username>
字段:-
需要判断长度,长度小于 \(1\) 时,说明字段为空,直接判断长度是否为 \(0\) 即可,由于下标从 \(0\) 开始,结束下标最多是 \(15\),所以判断 \(u.size()>=16\) 就行了。
-
字段中只允许大写,小写字母,数字和下划线,直接遍历一遍寻找违规字符即可。
-
代码:
void username(){ // 判断 username 是否合法 int l=u.size(); // l 记录长度,因为多次求长度,所以多开一个变量 if(l==0 || l>=16) N; // 判断长度是否超限 for(int i=0; i<l; i++){ // 遍历一遍,寻找是否有不允许字符 if(!(u[i]>='A' && u[i]<='Z' || u[i]>='a' && u[i]<='z' || u[i]>='0' && u[i]<='9' || u[i]=='_')) N; } }
-
-
<hostname>
字段:-
第一位不能为
.
,等于判断第一个被.
分割的段是否为空。 -
长度同
<username>
字段,不能为 \(0\) 或大于等于 \(32\),但是如果判断 \(s.size()>=32\) 会 \(WA\),改成 \(33\) 就行了,看上去是要算入@
符号。 -
遍历一遍查找违规字符,前提是当前这个字符不是
.
,那么我们就要先判断当前字符是.
的情况,如果是这样,那么下一位就不能是.
,因为每段长度不能为 \(0\)。还有一个问题:判断分割字段为空时,也就是是否有连续两个.
时,可能越界。我们可以在主函数中判断一下,这里暂时不解释。 -
代码:
void hostname(){ // 判断 hostname 是否合法 int l=h.size(); // 同 username(),记录长度 // 第一项为 '.',长度超限或为 0,都不行 // P.S. 长度好像要算 '@' 符号qwq if(h[0]=='.' || l==0 || l>=33) N; for(int i=0; i<l; i++){ // 因为主函数中已经确保最后一位不是 '.' 所以不会越界。照样找一遍不允许字符 if(h[i]=='.' && h[i+1]=='.' || // 每一段不能为空,也就是不能有相邻的 '.' h[i]!='.' && // 否则可以有 '.',不等于 '.' 时才判断是否有不允许字符 !(h[i]>='A' && h[i]<='Z' || h[i]>='a' && h[i]<='z' || h[i]>='0' && h[i]<='9' || h[i]=='_')) N; } }
-
-
<resource>
字段:-
如果长度为 \(0\),则代表没有该字段,返回即可,注意不是非法情况!
-
最后一项不能为
/
或.
。 -
算上字符
/
,长度不应大于等于 \(17\),为空情况前面已经判断了,所以无需判断。 -
遍历一遍查找不规范字符,同
<username>
字段。 -
代码:
void resoure(){ // 判断 resoure 是否合法 int l=r.size(); if(!l) return; // 没有 resoure 字段,直接返回 // 最后一项为 '/' 或 '.' 都不行 if(r[l-1]=='/') N; if(l>=17) N; // 算上 '/' 字符的长度不能大于等于 17 // 其余和 username() 一样,不再解释 for(int i=0; i<l; i++){ if(!(r[i]>='A' && r[i]<='Z' || r[i]>='a' && r[i]<='z' || r[i]>='0' && r[i]<='9' || r[i]=='_')) N; } }
-
-
主函数中:
-
首先,如果没有
@
字符肯定不行。 -
不管有没有
<resource>
,最后一位都不能为.
字符。 -
如果有
<resource>
字段,整个字段不能只有一个/
字符。 -
代码:
int main(){ cin >> s; // 初始化长度,'@' 符位置和 '/' 符位置 sz=s.size(),fd1=s.find('@'),fd2=s.find('/'); // 没有 '@' 符情况和最后一位为 '.' 的情况非法 if(fd1==string::npos || s[sz-1]=='.') N; u=s.substr(0,fd1); // username 串 h=s.substr(fd1+1,fd2-(fd1+1)); // hostname 串 if(fd2!=string::npos){ // 如果有 resource,就取,r 默认为空 if(s.substr(fd2,sz-fd2)=="/") N; // 判断 resource 只有 '/' 的情况 r=s.substr(fd2+1,sz-fd2); } // 判断各个字段是否有违规,只要找到了就直接输出并退出程序 username(),hostname(),resoure(); puts("YES"); // 都没找到,ok return 0; }
-
好了,只要上面这些都注意到了,这道毒瘤题也就圆满 \(AC\) 了qwq。
代码
#include <iostream>
#define N puts("NO"),exit(0) // 处理非法情况,只要非法就结束程序(其实是偷懒doge)
using namespace std;
int sz,fd1,fd2; // sz 存原串长度,fd1 存 '@' 符号的位置,fd2 存 '/' 符号的位置
string s,u,h,r; // 分别为:原串,username 串,hostname 串,resource 串
void username(){ // 判断 username 是否合法
int l=u.size(); // l 记录长度,因为多次求长度,所以多开一个变量
if(l==0 || l>=16) N; // 判断长度是否超限
for(int i=0; i<l; i++){ // 遍历一遍,寻找是否有不允许字符
if(!(u[i]>='A' && u[i]<='Z' ||
u[i]>='a' && u[i]<='z' ||
u[i]>='0' && u[i]<='9' ||
u[i]=='_')) N;
}
}
void hostname(){ // 判断 hostname 是否合法
int l=h.size(); // 同 username(),记录长度
// 第一项为 '.',长度超限或为 0,都不行
// P.S. 长度好像要算 '@' 符号qwq
if(h[0]=='.' || l==0 || l>=33) N;
for(int i=0; i<l; i++){
// 因为主函数中已经确保最后一位不是 '.' 所以不会越界。照样找一遍不允许字符
if(h[i]=='.' && h[i+1]=='.' || // 每一段不能为空,也就是不能有相邻的 '.'
h[i]!='.' && // 否则可以有 '.',不等于 '.' 时才判断是否有不允许字符
!(h[i]>='A' && h[i]<='Z' ||
h[i]>='a' && h[i]<='z' ||
h[i]>='0' && h[i]<='9' ||
h[i]=='_')) N;
}
}
void resource(){ // 判断 resource 是否合法
int l=r.size();
if(!l) return; // 没有 resource 字段,直接返回
// 最后一项为 '/' 或 '.' 都不行
if(r[l-1]=='/') N;
if(l>=17) N; // 算上 '/' 字符的长度不能大于等于 17
// 其余和 username() 一样,不再解释
for(int i=0; i<l; i++){
if(!(r[i]>='A' && r[i]<='Z' ||
r[i]>='a' && r[i]<='z' ||
r[i]>='0' && r[i]<='9' ||
r[i]=='_')) N;
}
}
int main(){
cin >> s;
// 初始化长度,'@' 符位置和 '/' 符位置
sz=s.size(),fd1=s.find('@'),fd2=s.find('/');
// 没有 '@' 符情况和最后一位为 '.' 的情况非法
if(fd1==string::npos || s[sz-1]=='.') N;
u=s.substr(0,fd1); // username 串
h=s.substr(fd1+1,fd2-(fd1+1)); // hostname 串
if(fd2!=string::npos){ // 如果有 resource,就取,r 默认为空
if(s.substr(fd2,sz-fd2)=="/") N; // 判断 resource 只有 '/' 的情况
r=s.substr(fd2+1,sz-fd2);
}
// 判断各个字段是否有违规,只要找到了就直接输出并退出程序
username(),hostname(),resoure();
puts("YES"); // 都没找到,ok
return 0;
}