『题解』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;
}
posted @ 2022-01-21 21:12  仙山有茗  阅读(35)  评论(0编辑  收藏  举报