2022年10月30日日曜日

文字列の検索・操作、文字列についての条件判定、アスキーコード、正規表現(2)


アスキーコードから文字へ

ABC252-A

問題概要

97から122までの数字が与えられるので、アスキーコードで対応する文字を出力せよ。

解答

n=gets.to_i
puts n.chr
    

とすればよいが、敢えてrubyのメソッドを使わなければ、

n=gets.to_i
n-=97
h={0 => "a",1 => "b",2 => "c",3 => "d",4 => "e",5 => "f",6 => "g",7 => "h",8 => "i",9 => "j",10 => "k",11 => "l",12 => "m",13 => "n",14 => "o",15 => "p",16 => "q",17 => "r",18 => "s",19 => "t",20 => "u",21 => "v",22 => "w",23 => "x",24 => "y",25 => "z",}
puts h[n]
    

となる。ちなみに、上のコードのハッシュは、下記のように生成した。やってみると意外にエスケープが面倒だった。

s="{"
26.times do |i|
    s+="#{i} => "
    s+='"'
    s+="#{(i+97).chr}"
    s+='",'
end
s+="}"
puts s
    

この時は本番不参加。Cで書いたのが以下。

int readint(char *s);
#define N1 5
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    //printf("%d\n",n);
    free(s1);
    printf("%c\n",n);
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

周期算、アスキーコード

ABC257-A

問題概要

英語アルファベットの大文字をn個ずつ並べた文字列がある。このm文字目を出力せよ。

解答

アルファベットを一旦数字に置き換えて、例えばn=3なら、000111222333...(25)(25)(25)のように並んでいるとする。こうすると、数字i(i=0,1,2,..,25)は、i*n番目からi*(n+1)-1番目まで並んでいることになる。つまり、ある数字がm番目であれば、mをnで割った商はiである。

よって、問題で与えられたmは1-originであることを考慮してこれから1を引いてnで割り、それに65を足した数字が求める文字のアスキーコードになる。

n,m=gets.chomp.split(" ").map(&:to_i)
t=(m-1)/n
puts (t+65).chr
    

これが本番で提出したコード。Cで書いたのが以下。

int readint(char *s);
#define N1 10
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int x=readint(s1+j);
//    printf("n=%d x=%d\n",n,x);
    printf("%c\n",65+(x-1)/n);
    free(s1);
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

文字列の切り取り

ABC264-A

問題概要

"atcoder"のl文字目からr文字目までを出力せよ。

解答

l,r=gets.chomp.split(" ").map(&:to_i)
puts "atcoder".slice(l-1,r-l+1)
        

l文字目から、(r-l+1)文字を出力している。

この時は本番不参加。

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if (s1==NULL){
        printf("memory allocation to s1 failed.\n");
        exit(1);
    }
    char* ts1=fgets(s1,N1,stdin);
    if (ts1==NULL){
        printf("fgets(s1) failed.\n");
        exit(1);
    }
    int l=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int r=readint(s1+j);
    free(s1);
    char* str="atcoder";
//    printf("l=%d r=%d\n",l,r);
//    printf("%s\n",str);
    for(int i=l-1;i<r;i++){
        printf("%c",*(str+i));
    }
    printf("\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

長い文字列中の先頭部分にに短い文字列が完全に含まれているか判定する。

ABC268-B

問題概要

文字列s,tが与えられる。(sよりもtの方が長い) sの文字数をnとすると、sがtの最初のn文字に一致するかどうか判定せよ。

解答

s=gets.chomp
t=gets.chomp
a=[]
n=t.size
(0..n).each do |i|
    a[i]=t.slcie(0,i)
end
ans="No"
(0..n).each do |i|
    if a[i]==s
        ans="Yes"
        break
    end
end
puts ans
    

上が本番で提出したコードなのだが、自分で読み返してみて、随分とひねったことをやっているな、と思った。

長い方の文字列の文字数をnとして、その、先頭から長さ1..n文字を切り取った部分文字列を作り、それが短い方の文字列と一致すれば良しとしている。

しかしこれは単純に次のようにすれば済むことであった。

s=gets.chomp.split("")
t=gets.chomp.split("")
s.size.times do |i|
    if s[i]!=t[i]
        puts "No"
        exit
    end
end
puts "Yes"
    

また、rubyには文字列クラスにstart_with?という便利なメソッドが用意されているので、そちらを使ってもよい。

s=gets.chomp
t=gets.chomp
if t.start_with?(s)
    puts "Yes"
else
    puts "No"
end
    

Cで書いたのが以下。

int slen(const char *s);
#define N1 102
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    char* s2=(char*)malloc(sizeof(char)*N1);
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,N1,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
//    printf("%s",s1);
//    printf("%s",s2);
    int n=slen(s1);
//    printf("n=%d\n",n);
    int i=0;
    while(i<n){
        if(*(s2+i)!=*(s1+i)){
            break;
        }
        i++;
    }
//    printf("i=%d\n",i);
    if(i==n)printf("Yes\n");
    else printf("No\n");
    return 0;
}
int slen(const char *s){
    int i=0;
    while(*(s+i)>=97 && *(s+i)<=122)i++;
    return i;
}
    

slen関数を、最初は、 while(*(s+i)!=0 && *(s+i)!="\n")i++; と書いていたのだが、こうすると実際の文字数よりも1大きい値が返ってしまうため、本問ではsはローマ字小文字のみからなることを考え、上のように書き直した。


トランプのカードとして適切か判定

ABC277-B

問題概要

2つの文字からなる文字列がn個与えられる。その文字列の配列の要素に、

  • 一文字目がH, D, C, Sのいずれかでない。
  • 二文字目が、A,1,2,3,4,5,6,7,8,9,T,J,Q,Kのいずれかでない。
  • 同じ文字列が二回以上出現している。

のいずれか一つ以上にあてはまるものがあれば"No"を、どれにも当てはまらなければ"Yes"を出力せよ。

解答

n=gets.to_i
a=[]
a1=["H","D","C","S"]
a2=["A" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "T" , "J" , "Q" , "K"]
n.times do |i|
    t=gets.chomp.split("")
    a.push(t)
end
n.times do |i|
    if !(a1.include?(a[i][0])) || !(a2.include?(a[i][1]))
        puts "No"
        exit
    end
end
aa=a.uniq
if a.size != aa.size
    puts "No"
    exit
end
puts "Yes"
    

これが本番で提出したコード。

一文字目、二文字目として許容される文字の配列をそれぞれ作り、もし与えられた文字列の一文字目、二文字目がその配列に含まれていなければNoを出力して終了。そのループを抜けたら、今度は与えられた文字列の配列から重複したものを除いた配列を作り、その大きさともとの配列の大きさが一致していなければ、Noを出力して終了。もしそれが終わっても終了していなければ、適切な文字列の配列だということなので、Yesを出力する。

論理判定の書き方はいろいろ考えられるけれど、これが一番楽かなと思った。一つ目の条件と二つ目の条件だけなら正規表現で判定することもできるが。

Cで書いたのが以下。

int readint(char *s);
#define N1 4
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    free(s1);
    int *ref=(int*)malloc(sizeof(int)*52);
    for(int i=0;i<52;i++){
        *(ref+i)=0;
    }
    for(int i=0;i<n;i++){
        int num;
        char* s2=(char*)malloc(sizeof(char)*N1);
        if(s2==NULL){
            printf("memory allocation to s2(%d) failed\n",i);
            exit(1);
        }
        char *ts2=fgets(s2,N1,stdin);
        if(ts2==NULL){
            printf("fgets(s1(%d)) failed\n",i);
            exit(1);
        }
        if(*(s2)!=72 && *(s2)!=68 && *(s2)!=67 && *(s2)!=83){
            printf("No\n");
            exit(0);
        }
        if(*(s2+1)==65){
            num=1;
        }else if(*(s2+1)==84){
            num=10;
        }else if(*(s2+1)==74){
            num=11;
        }else if(*(s2+1)==81){
            num=12;
        }else if(*(s2+1)==75){
            num=13;
        }else if(*(s2+1)>=50 && *(s2+1)<=57){
            num=(int)(*(s2+1)-48);
        }else{
            printf("No\n");
            exit(0);  
        }
        if(*(s2)==72){
            if(*(ref+num)==1){
                printf("No");
                exit(0);
            }else{
                *(ref+num)=1;
            }
        }else if(*(s2)==68){
            if(*(ref+num+13)==1){
                printf("No");
                exit(0);
            }else{
                *(ref+num+13)=1;
            }
        }else if(*(s2)==67){
            if(*(ref+num+26)==1){
                printf("No");
                exit(0);
            }else{
                *(ref+num+26)=1;
            }
        }else if(*(s2)==83){
            if(*(ref+num+39)==1){
                printf("No");
                exit(0);
            }else{
                *(ref+num+39)=1;
            }
        }

    }
    printf("Yes\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

2回目なので、判定のしかたもrubyで書いた時とは若干変えて、同じものがないかどうかの判定については、在りうる52種類に対応する表を作り、あるカードを読み込んだらそのカードに対応する場所に既にチェックが付いていないかどうかを調べ、ついていたらNoを出力して終了、ついていなければその場所にチェックマークを付けて次に進むというようにした。


長い文字列中に短い文字列が完全に含まれているか判定する。

ABC279-B

問題概要

文字列s,tが与えられる。tがsの一部分に一致するかどうか判定せよ。

解答

s=gets.chomp.split("").to_a
t=gets.chomp.split("").to_a
ss=s.size
ts=t.size
(ss-ts+1).times do |i|
    flag=true
    ts.times do |j|
        if s[i+j]!=t[j]
            flag=false
            break
        end
    end
    if flag==true
        puts "Yes"
        exit
    end
end
puts "No"
    

これが本番の提出コード

sの先頭から、二重ループを回して一致するところがあるかどうか探している。

外側のループで、まずsの側の開始位置を0番目から「(sの長さ)-(tの長さ)+1」回動かしている。sとtの長さが同じ時も1回は判定する必要があるため、回数は(sの長さ)-(tの長さ)に1を足す必要がある。

あとは、内側のループで、その開始位置から(tの長さ)分のsの部分文字列がtと一致しているか見ていく。一文字でも一致していないところであれば、flagをfalseにしてループを抜ける。内側のループを抜けてもflagがtrueのままであれば、完全に一致したということなので、Yesを出力して終了。もし外側のループを抜けた時点で終了していなかったら、どの開始位置でも一致しなかったということなので、Noを出力している。

もっとも、これは次のように正規表現を使って解いた方が早かった。

s=gets.chomp
t=gets.chomp
if s.match(t) 
    puts "Yes"
else 
    puts "No"
end
    

Cで書いたのが以下。

int slen(const char *s);
#define N1 102
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    char* s2=(char*)malloc(sizeof(char)*N1);
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,N1,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
//    printf("%s",s1);
//    printf("%s",s2);
    int s_length=slen(s1);
    int t_length=slen(s2);
//    printf("n=%d\n",n);
    int i=0;
    for(;i<s_length-t_length+1;i++){
        int flag=1;
        for(int j=0;j<t_length;j++){
            if(*(s2+j)!=*(s1+i+j)){
                flag=0;
                break;
            }
        }
        if(flag==1){
            printf("Yes");
            exit(0);
        }
    }
    printf("No\n");
    return 0;
}
int slen(const char *s){
    int i=0;
    while(*(s+i)>=97 && *(s+i)<=122)i++;
    return i;
}
    

前回やった268-Bに少し手を加えただけで済んだ。


ID等として適当かどうか判定

ABC281-B

問題概要

英大文字と数字からなる文字列 S が与えられるので、S が以下の条件を満たすか判定せよ。

  1. s[0]とs[7]は英大文字
  2. s[1]は1から9までの数字
  3. s[2]からs[6]までは0から9までの数字
s=gets.chomp
if /\A[A-Z]{1}[1-9]{1}\d{5}[A-Z]{1}\z/.match(s)
    puts "Yes"
else
    puts "No"
end
    

本番では上のようにrubyの正規表現機能を使って解いたのだが、一文字ずつアスキーコードに変換して判定することもできる。

s=gets.chomp
if s.size != 8
    puts "No"
    exit
end
s1=s[0].ord-65
if s1<0 || s1>26
    puts "No"
    exit
end
s2=s[1].ord-48
if s2<1 || s2>9
    puts "No"
    exit
end
(2..6).each do |i|
    ss=s[i].ord-48
    if ss<0 || ss>9
        puts "No"
        exit
    end
end
s8=s[7].ord-65
if s8<0 || s8>26
    puts "No"
    exit
end
puts "Yes"
    

英語アルファベットの大文字のアスキーコードはそこから65を引くと0-25の数字に、0-9の数字はのアスキーコードはそこから48を引くと0-9の数字になるので、条件に合うかどうか一文字ずつ調べている。

Cで書いたのが以下。

int slen(const char *s);
#define N1 12
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    if(slen(s1)!=8){
        printf("No");
        exit(0);
    }
    if((*s1<65 || *s1>90) || (*(s1+1)<49 || *(s1+1)>57) || (*(s1+7)<65 || *(s1+7)>90)){
        printf("No");
        exit(0);
    }
    for(int i=2;i<7;i++){
        if(*(s1+i)<48 || *(s1+i)>57){
            printf("No");
        exit(0);
        }
    }
    printf("Yes\n");
    return 0;
}
int slen(const char *s){
    int i=0;
    while((*(s+i)>=65 && *(s+i)<=90) || (*(s+i)>=48 && *(s+i)<=57))i++;
    return i;
}
    

アスキーコードの列から文字列へ

ABC282-A

問題概要

Aからk文字目までのアルファベット大文字を順に連結して出力せよ。

k=gets.to_i
a=(0...k).to_a.map{|a| a+65}
puts a.pack("c*")
    

これが本番で提出したコード

0からk-1までの数字の配列を作り、その各要素に65を足していくと、Aから始めてk番目のアルファベットの文字までに対応するアスキーコードの配列ができる。あとは、それを文字列に変換して出力。

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 8
int main(void){
    char *s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int k=readint(s1);
    free(s1);
    for(int i=0;i<k;i++){
        printf("%c",65+i);
    }
    printf("\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

モードの切り替え

ABC282-C

問題概要

"を偶数個含む文字列が与えられる。その2i番目と2i+1番目(i=0,1,...)の"の間にある文字を「内側にある」といい、そうでない文字を「外側にある」という。外側にある","を"."に置き換えた文字列を出力せよ。

しばらくC問題には手を付けていなかったので、今回も最初はB問題が終わった時点でほかのことを始めていたのだが、ふとみとる簡単な問題だったので解いた。

解答

以下が、本番で提出したコード。

n=gets.to_i
s=gets.chomp.split("").to_a
mode=0
n.times do |i|
    if mode==0 && s[i]=="\""
        mode=1
    elsif mode==0 && s[i]==","
        s[i]="."
    elsif mode==1 && s[i]=="\""
        mode=0
    end
end
puts s.join("")
    

内側か外側かでモードを切り替えて処理すればよい。あとは、"のエスケープ処理が要注意なくらいか。C問題とは思えない簡単さだった。

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 8
int main(void){
    char *s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    free(s1);
//    printf("n=%d\n",n);
    char *s2=(char*)malloc(sizeof(char)*(n+2));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,n+2,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
//    printf("%s\n",s2);
    int flag=0;
    for(int i=0;i<n;i++){
        char c=*(s2+i);
        if (flag==0 && c==34){
            flag=1;
//            printf("mode changed from out to in(%d)\n",i);
        }else if (flag==1 && c==34){
            flag=0;
//            printf("mode changed from in to out(%d)\n",i);
        }else if (flag==0 && c==44){
            c=46;
        }
        printf("%c",c);
    }
    printf("\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

ABC285-B

問題概要

長さnの文字列sが与えられる。i=1,2,…,N−1 それぞれについて、「全ての 1≤k≤l を満たす整数 k について、s[k]!=s[k+i]を満たす。」という条件を満たす最大の非負整数lを求めよ。

解答

どういう意味のある問題なのかよくわからなかったが、とりあえず問題の条件の通りにコードを書いてみる。

n=gets.to_i
s=gets.chomp
(1...n).each do |i|
    flag=true
    l=0
    (n-1).times do |j|
        if i+j>n-1
            break
        end
        if s[j]==s[j+i]
            flag=false
            break
        else
            l=j+1
        end
    end
    puts l
end
    

上が本番で提出したコード。結果は、TLE(Sample: AC x 1, All AC x 18, TLE x 2)となった。

各iについて、lの初期値を0とし、i+jがnより小さいことを満たすj=0..n-2について、s[j+i]がs[j]と等しければそこで止め、そうでなければj+1の値をlに代入していっている。

nの最大値は5000で二重ループを回し、中で比較を2回、代入を1回しているから、計算量は5000*5000*3=7.5*10^7程度。これでTLEになるのだろうか。

同じコードをCで書いてみる。

#include<stdio.h>
#include<stdlib.h>
int main(void){
    int n;
    scanf("%d",&n);
    char* s=(char*)malloc(sizeof(char)*n);
    scanf("%s\n",s);
    int l=0;
    for(int i=1;i<n;i++){
        int l=0;
        for(int j=0;j<n-1;j++){
            if (i+j>n-1)break;
            else if (s[j+i]==s[j])break;
            else l=j+1;
        }
        printf("%d\n",l);
    }
    return 0;
}    

論理的には全く同じコートどのはずなのだが、rubyでやったときは実行時間が2206ms, メモリが14232KB, Cだと実行時間が16ms, メモリが1712KBとなっている。Cとrubyの速さの違いだけでは説明できない気がする。


置換

ABC286-B

問題概要

与えられた文字列中のnaをnyaと置換せよ

解答

n=gets.to_i
s=gets.chomp
s=s.gsub("na","nya")
puts s
    

rubyの置換メソッドを使えばあっという間に終わる。A問題より簡単だ。

だが、これで終わるのはつまらないので、今度は敢えてこのメソッドを使わずに書いてみる。

こちらはCで書いた。前回、同じコードなのにRubyで書くとTLEになり、CだとACになったことがあったたため、少しCで書く練習もしておく。

#include<stdio.h>
#include<stdlib.h>
int main(void){
    int n;
    scanf("%d",&n);
    char* s=(char*)malloc(sizeof(char)*n);
    scanf("%s\n",s);
    int sidx,tidx,count;
    sidx=tidx=count=0;
    for(int i=0;i<n;){
        if(*(s+i)=='n' && *(s+i+1)=='a'){
            count++;
            i+=2;
        }else{
            i++;
        }
    }
    char* t=(char*)malloc(sizeof(char)*(n+count));
    for(;sidx<n;){
        if(*(s+sidx)=='n' && *(s+sidx+1)=='a'){
            *(t+tidx)='n';
            *(t+tidx+1)='y';
            *(t+tidx+2)='a';
            sidx+=2; tidx+=3;
        }else{
            *(t+tidx++)=*(s+sidx++);
        }
    }
    printf("%s\n",t);
    return 0;
}
    

Rubyだと超の付く簡単さだったが、Cだと途端にやや面倒になる。一旦入力された文字列を調べた後でないと置換後の文字列の長さが分からないので、まず置換すべき部分が何カ所あるかを調べ、それを元の文字列の長さに足した大きさのメモリを確保、その後に実際の置換を行うという二段構えでやらねばならない。

比較すると、以下のようになる。

コード長実行時間メモリ
Ruby555814356
C71851660
実行時間とメモリでは10倍程度の差が出た。


後で考え直してみると、上で書いたような面倒なことをする必要はなかったので、やり直したのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 6
int main(void){
    char *s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    free(s1);
//    printf("n=%d\n",n);
    char *s2=(char*)malloc(sizeof(char)*(n+2));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,n+2,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
//    printf("%s\n",s2);
    int i=0;
    while(i<n-1){
        if(*(s2+i)==110 && *(s2+i+1)==97){
            printf("%c",110);
            printf("%c",121);
            printf("%c",97);
            i+=2;
        }else{
            printf("%c",*(s2+i));
            i++;
        }
    }
    if(i==n-1){
        printf("%c",*(s2+n-1));
    }
    printf("\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

並べ替え

ABC288-B

問題概要

n個の文字列が与えられるのので、その初めのk個を辞書順に並べ替えて出力せよ。

解答

n,k=gets.chomp.split(" ").map(&:to_i)
a=Array.new(k,"")
k.times do |i|
    s=gets.chomp
    a[i]=s
end
a.sort!
k.times do |i|
    puts a[i]
end    

上が本番で提出したコード。Cで書いたのが以下。

#define N1 9
#define N2 10
#include<stdio.h>
#include<stdlib.h>
void qsort_2(char* s, int l, int r, int length);
int scmp1(char* s, char* t, int n);
int readint(char *s);
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int k=readint(s1+j);
//    printf("n=%d k=%d\n",n,k);
    char* s2=(char*)malloc(sizeof(char)*(N2+1)*k);
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    for(int i=0;i<k;i++){
        char* s3=(char*)malloc(sizeof(char)*(N2+2));
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        char *ts3=fgets(s3,N2+2,stdin);
        if(ts3==NULL){
            printf("fgets(s3(%d)) failed\n",i);
            exit(1);
        }
        j=0;
        while(*(s3+j)>=97 && *(s3+j)<=122){
            *(s2+(N2+1)*i+j)=*(s3+j);
            j++;
        }
        *(s2+(N2+1)*i+j)=0;
    }
    qsort_2(s2,0,k-1,(N2+1));
    for(int i=0;i<k;i++){
        printf("%s\n",(s2+(N2+1)*i));
    }
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
int scmp1(char* s, char* t, int n){
    int i=0;
    while(1){
        if (*(s+i)==0 && *(t+i)==0) return 0;
        else if (*(t+i)!=*(s+i)) return (*(s+i)-*(t+i));
        else i++;
        if(i>=n){
            printf("s=%s, t=%s, i=%d\n",s,t,i);
            printf("Comparing failed.\n");
        }
    }
}
void qsort_2(char* s, int l, int r, int length){
    int left, right;
    char* div;
    if (l>=r)return;
    div=(s+l*length);
    for(left=l,right=r;left<right;){
        while(left<=right && scmp1((s+length*left),div,length)<=0)left++;
        while(left<=right && scmp1((s+length*right),div,length)>0)right--;
        if(left<right){
            char *temp=malloc(sizeof(char)*length);
            if(temp==NULL){
                printf("memory allocation to temp(inside qsort_2) failed.\n");
                exit(2);
            }
            int i=0;
            while(*(s+length*left+i)!=0){
                *(temp+i)=*(s+length*left+i);
                i++;
            }
            *(temp+i)=0;
            i=0;
            while(*(s+length*right+i)!=0){
                *(s+length*left+i)=*(s+length*right+i);
                i++;
            }
            *(s+length*left+i)=0;
            i=0;
            while(*(temp+i)!=0){
                *(s+length*right+i)=*(temp+i);
                i++;
            }
            *(s+length*right+i)=0;
            free(temp);
        }
    }
    char *temp2=malloc(sizeof(char)*length);
    if(temp2==NULL){
        printf("memory allocation to temp2(inside qsort_2) failed.\n");
        exit(2);
    }
    int i=0;
    while(*(s+length*l+i)!=0){
        *(temp2+i)=*(s+length*l+i);
        i++;
    }
    *(temp2+i)=0;
    i=0;
    while(*(s+length*right+i)!=0){
        *(s+length*l+i)=*(s+length*right+i);
        i++;
    }
    *(s+length*l+i)=0;
    i=0;
    while(*(temp2+i)!=0){
        *(s+length*right+i)=*(temp2+i);
        i++;
    }
    *(s+length*right+i)=0;
    free(temp2);
    qsort_2(s,l,right-1,length);
    qsort_2(s,right+1,r,length);
}
    

ソートアルゴリズムにはクイックソートを使った。クイックソートを実装するのも初めてだったし、文字列の配列の並べ替えをするのも初めてだったし、自作の文字列比較関数を(単に等しいかどうかの比較ではなく)大小比較に使うのも初めてだった。初めて尽くしだったから、うまく動くコードが書けるかどうか半ば諦めながら書いが、以外にあっさり完成させることができた。


ABC293-A

問題概要

長さnが偶数の文字列が与えられる。2*i番目と2*i+1番目(i=0,...,n/2)の文字を入れ替えた文字列を出力せよ。

解答

s=gets.chomp
(s.size/2).times do |i|
    s[2*i],s[2*i+1]=s[2*i+1],s[2*i]
end
puts s
    

上が本番で提出したコード。以下はCで書いたもの。

void swap_char(char *a,char *b);
int is_nonletter(char *c);
#include<stdio.h>
#include<stdlib.h>
#define N 1002
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N-1,stdin);
    for(int i=0;!is_nonletter(s+i);i+=2){
        swap_char(s+i,s+i+1);
    }
    printf("%s\n",s);
    return 0;
}
void swap_char(char *a,char *b){
    char temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}
    

大文字に

ABC292-A

問題概要

与えられた文字列を大文字にして出力せよ。

解答

以下が本番で提出したコード。

s=gets.chomp
puts s.upcase
    

Cの自作関数でやったのが以下。

int is_lower(char *c);
int is_alphabet(char *c);
int is_nonletter(char *c);
int ucase(char *c);
int str_to_ucase(char* c);
#include<stdio.h>
#include<stdlib.h>
#define N 1002
int main(void){
    char* s=(char*)malloc(sizeof(char)*(N));
    fgets(s,N-1,stdin);
    str_to_ucase(s);
    printf("%s\n",s);
    return 0;
}
int is_lower(char *c){
    if(*c>=97 && *c<=122)return 1;
    else return 0;
}
int is_alphabet(char *c){
    if ((*c>=65 && *c<=90) || (*c>=97 && *c<=122)){
        return 1;
    }else{
        return 0;
    }
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}
int ucase(char *c){
    if(*c>=97 && *c<=122){
        return (*c-32);
    }
    else return -1;
}
int str_to_ucase(char* c){
    int i=0;
    while(!is_nonletter(c+i)){
        if(is_lower(c+i)){
            int t;
            t=*(c+i)=ucase(c+i);
            if (t>=65 && t<=90)*(c+i)=t;
            else printf("something is wrong\n");
        }
        i++;
    }
}
    

ABC291-A

問題概要

与えられた文字列の中に一つだけ英大文字がある。それが何番目か出力せよ。

解答

s=gets.chomp
s.size.times do |i|
    if s[i].ord>=65 && s[i].ord<=90
        puts i+1
    end
end
    

これが本番で提出したコード。Cで書いたのが以下。

int slen(char* s);
int is_upper(char *c);
int is_nonletter(char *c);
#include<stdio.h>
#include<stdlib.h>
#define N 102
int main(void){
    char* s=(char*)malloc(sizeof(char)*(N));
    fgets(s,N-1,stdin);
    int sz=slen(s);
    for(int i=0;i<sz;i++){
        if (is_upper(s+i)){
            printf("%d\n",i+1);
            exit(0);
        }
    }
    return 0;
}
int slen(char* s){
    int i=0;
    while(!is_nonletter((s+i)))i++;
//    printf("%s: %d\n",s,i);
    return i;
}
int is_upper(char *c){
    if(*c>=65 && *c<=90)return 1;
    else return 0;
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}
    

ABC294-B

問題概要

0~26の整数を要素とするh*wの行列が与えられる。0は.に置き換え、1~26は対応するアルファベットの大文字に置き換えて出力せよ。

解答

h,w=gets.chomp.split(" ").map(&:to_i)
b=[]
c=Array.new(h){Array.new(w,"")}
h.times do |i|
    b[i]=gets.chomp.split(" ").map(&:to_i)
    w.times do |j|
        if b[i][j]==0
            c[i][j]='.'
        else
            c[i][j]=(b[i][j]+64).chr
        end
    end
    puts c[i].join("")       
end
    

上が本番で提出したコード。以下はCで書いたもの。

#include<stdio.h> #include<stdlib.h> #define N 1002 int str_toi(char* s); int* scan_int(char* s,int *w); int split_line(char* s,int* a,int *b); void print_ivector(int* a,int n); int main(void){ int h,w; char* hw=(char*)malloc(sizeof(char)*N); fgets(hw,N,stdin); int i=0; h=str_toi(hw); while(*(hw+i)!=' ')i++; i++; w=str_toi(hw+i); free(hw); for(int i=0;i<h;i++){ char* s=(char*)malloc(sizeof(char)*N); fgets(s,N,stdin); int ws; int* a=(int*)malloc(sizeof(int)*100); a=scan_int(s,&ws); // print_ivector(a,ws); for(int j=0;j<ws;j++){ if(*(a+j)==0)printf("%c",'.'); else printf("%c",*(a+j)+64); } printf("\n"); } return 0; }

ABC295-A

問題概要

与えられた文字列が、["and", "not", "that", "the", "you"]のいずれかを含むか。

解答

n=gets.to_i
a=gets.chomp.split(" ")
b=["and", "not", "that", "the", "you"]
flag=false
a.each do |i|
    b.each do |j|
        if i==j
            puts "Yes"
            exit
        end
    end
end
puts "No"
    

上が本番で提出したコード。以下はCで書いたもの。(第一次)

void input_iarray(char* s,int* a,int n);
void input_imatrix(int* a,int n,int m);
void print_imatrix(int* a,int n,int m);
void print_ivector(int* a,int n);
void print_ivector2(int* a,int n);
int is_lower(char *c);
int is_upper(char *c);
int is_alphabet(char *c);
int is_digit(char *c);
int is_dw(char *c);
int is_otherchars(char *c);
int is_nonletter(char *c);
int is_separator(char *c);
int lcase(char *c);
int ucase(char *c);
int ucase(char *c);
int onechar_toi(char c);
int str_toi(char* s);
double str_tof(char *s);
int slen(char* s);
int scmp(char *s, char *t);
void str_cpy(char *s, const char *t);
void str_cat(char *s, const char *t);
int split_line(char* s,int* a,int *b);
int splitln1(char*s,int* a,int*b,char sep);
int splitln2(char*s,int* a,char sep);
int* scan_int(char* s,int *w);
void scan_int2(char* s,int* ret,int n);
void swap_int(int *a,int *b);
void swap_char(char *a,char *b);
void swap_double(double *a,double *b);
int max(int a,int b);
int min(int a,int b);
int gcd(int a, int b);
#include<stdio.h>
#include<stdlib.h>
#define N 10002
int main(void){
    int n;
    scanf("%d",&n);
    getchar();
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N-2,stdin);
    char* ss[5]={"and", "not", "that", "the", "you"};
    int* a=(int*)malloc(sizeof(int)*n);
    splitln2(s,a,' ');
//    print_ivector(a,n);
    for(int i=0;i<n;i++){
//        printf("source:%s\n",s+*(a+i));
        for(int j=0;j<5;j++){
//            printf("target:%s\n",*(ss+j));
            if (scmp(*(ss+j),s+*(a+i))==0){
                printf("Yes\n");
                exit(0);
            }
        }
    }
    printf("No\n");
    return 0;
}
void input_imatrix(int* a,int n,int m){
    for(int i=0;i<n;i++){
        for(int j=0;j<m-1;j++){
            scanf("%d ",a+i*m+j);
        }
        scanf("%d\n",a+i*m+m-1);
    }
}
void print_imatrix(int* a,int n,int m){
    for(int i=0;i<n;i++){
        for(int j=0;j<m-1;j++){
            printf("%d ",*(a+i*m+j));
        }
        printf("%d\n",*(a+i*m+m-1));
    }
}
void print_ivector2(int* a,int n){
    for(int i=0;i<n;i++){
        printf("%d\n",*(a+i));
    }
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
int is_lower(char *c){
    if(*c>=97 && *c<=122)return 1;
    else return 0;
}
int is_upper(char *c){
    if(*c>=65 && *c<=90)return 1;
    else return 0;
}
int is_alphabet(char *c){
    if ((*c>=65 && *c<=90) || (*c>=97 && *c<=122)){
        return 1;
    }else{
        return 0;
    }
}
int is_digit(char *c){
    if (*c>=48 && *c<=57)return 1;
    else return  0;
}
int is_dw(char *c){
    if ((*c>=48 && *c<=57) || (*c>=65 && *c<=90) || (*c>=97 && *c<=122))return 1;
    else return  0;
}
int is_otherchars(char *c){
    if ((*c>=32 && *c<=47) || (*c>=58 && *c<=64) || (*c>=91 && *c<=96) || (*c>=123 && *c<=126))return 1;
    else return  0;
}
int is_separator(char *c){
    if(*c==' ' || *c== '\t' || *c== ',' || *c=='\n' || *c=='\0'){
        return 1;
    }else{
        return 0;
    }
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}

int lcase(char *c){
    if(*c>=65 && *c<=90){
        return (*c+32);
    }
    else return -1;
}
int ucase(char *c){
    if(*c>=97 && *c<=122){
        return (*c-32);
    }
    else return -1;
}
int onechar_toi(char c){
    if (c>=48 && c<=57){
        return c-48;
    }else return -1;
    
}
int str_toi(char* s){
    int t=0,i=0;
    if (*s=='-')i++;
    while(*(s+i)>=48 && *(s+i)<=57){
        t*=10;
        t+=*(s+i)-48;i++;
    }
    if (*s=='-')t*=(-1);
    //printf("%s: %d\n",s,t);
    return t;
}
double str_tof(char *s){
    int i=0,j=1;double t=0.0;
    if (*s=='-')i++;
    while(*(s+i)>=48 && *(s+i)<=57){
        t*=10;
        t+=*(s+i)-48;i++;
    }
    if(*(s+i)=='.')i++;
    while(*(s+i)>=48 && *(s+i)<=57){
        j*=10;
        t+=(*(s+i)-48)/(double)j;i++;
    }
    if (*s=='-')t*=(-1);
    return t;
}
int slen(char* s){
    int i=0;
    while(!is_nonletter((s+i)))i++;
    printf("%s: %d\n",s,i);
    return i;
}
int scmp(char *s, char *t){
    int i=0;
    //printf("s=%s,t=%s\n",s,t); 
    int flag=4;
    while(1){
        if (is_nonletter(s+i) && is_nonletter(t+i))flag=0;
        else if (is_nonletter(s+i) && !is_nonletter(t+i))flag=-2;
        else if (!is_nonletter(s+i) && is_nonletter(t+i))flag=2;
        else if(*(s+i)>*(t+i))flag=1;
        else if(*(s+i)<*(t+i))flag=-1;
        else if(*(s+i)==*(t+i))i++;
        else flag=-2;
        /*
        switch(flag){
            case 0:
            printf("s==t\n");
            break;
            case -2:
            printf("s is shorter than t\n");
            break;
            case 2:
            printf("s is longer than t\n");
            break;
            case -1:
            printf("s comes before t\n");
            break;
            case 1:
            printf("s comes after t\n");
            break;
            case 4:
            printf("seeing next letter\n");
            break;
            default:
            printf("case was not assinged\n");
            printf("Unanticipated situation(1)\n");
        }*/
        if (flag!=4)return flag;
    }
    return flag;
}
void str_cpy(char *s, const char *t){
    int i=0;
    while(*(t+i)!=0){*(s+i)=*(t+i);i++;}
    ++i;*(s+i)=0;
}
void str_cat(char *s, const char *t){
    int i=0;int j=0;
    while(*(s+i)!=0)i++;
    i--;
    while(*(t+j)!=0){
        *(s+i)=*(t+j);i++;j++;
    }
    *(s+i)=0;
}
int split_line(char* s,int* a,int *b){
    int i,w,flag,fidx,c,hidx;
    i=w=flag=fidx=c=hidx=0;
    int lidx=-1;
    for(i=0;i<N;i++){
        if(!is_nonletter(s+i)){
            fidx=i;flag=1;break;
        }
    }
    if(!flag){
        printf("line does not include any words.(in function)\n");
        return 0;
    }
    for(i=N-1;i>0;i--){
        if(*(s+i)==10)*(s+i)=0;
        if(!is_nonletter(s+i)){
            lidx=i;break;
        }
        if(i==1 && !is_nonletter(s))lidx=0;
    }
    if(lidx==-1){
        printf("cannot find last index.\n");
        return -1;
    }
    for(i=fidx;i<N;){
        if(!is_nonletter(s+i)){
            if(i>0 && is_nonletter(s+i-1)){
                hidx=i;
                *(a+w)=i;c=0;
            }
            c++;i++;
        }else if(is_nonletter(s+i)){
            if(!is_nonletter(s+i-1)){
                *(b+w)=c;w++;
            }
            if (i==lidx+1)break;
            while(i<lidx && is_nonletter(s+i)){
                *(s+i)=0;i++;
            }
        }
    }
    if(i<=lidx){
        printf("i does not reach last index.\n");
        return -2;
    }else {
        return w;
    }
}
int splitln1(char*s,int* a,int*b,char sep){
    int i,c,w,hidx;
    i=c=w=hidx=0;
    *a=0;
    while(*(s+i)!='\n' && *(s+i)!=0){
        if(*(s+i)==sep){
            *(b+w)=c;w++;i++;
            *(a+w)=i;c=0;
        }else{
            i++;c++;
        }
    }
    *(b+w)=c;w++;
    return w;
}
int splitln2(char*s,int* a,char sep){
    int i,w;
    i=w=0;
    *a=0;
    while(*(s+i)!='\n' && *(s+i)!=0){
        if(*(s+i)==sep){
            w++;i++;
            *(a+w)=i;
        }else{
            i++;
        }
    }
    w++;
    return w;
}
int* scan_int(char* s,int *w){
        int* a=(int*)malloc(sizeof(int)*N);//i番目の文字列の開始インデックス
        int* b=(int*)malloc(sizeof(int)*N);//i番目の文字列の文字数
        *w=splitln1(s,a,b,' ');
        int* ret=(int* )malloc(sizeof(int)*(*w));
        for(int i=0;i<*w;i++){
            *(ret+i)=str_toi(s+*(a+i));
        }
        free(a);free(b);
        return ret;
}
void scan_int2(char* s,int* ret,int n){
        int* a=(int*)malloc(sizeof(int)*N);//i番目の文字列の開始インデックス
        int w=splitln2(s,a,' ');
        if (n!=w)printf("n!=w\n");
        for(int i=0;i<w;i++){
            *(ret+i)=str_toi(s+*(a+i));
        }
        free(a);
}
void swap_int(int *a,int *b){
    int temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
void swap_char(char *a,char *b){
    char temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
void swap_double(double *a,double *b){
    double temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
int max(int a,int b){
    return a>b ? a : b;
}
int min(int a,int b){
    return a<b ? a : b;
}
int gcd(int a, int b){
    int x,y;
    if(b>a){
        x=b;y=a;
    }else{
        x=a;y=b;
    }
    while(y!=0){
        int r;
        r=x%y;
        x=y;
        y=r;
    }
    return x;
}
void input_iarray(char* s,int* a,int n){
    int i=0;
    int aidx=0; 
    for(int i=0;i<n;){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
}
    

上のコードでもACになったのだが、このコードは使わない関数も多数含んでおり長すぎるので、やり直したのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 5010
int main(void){
    char* s=(char*)malloc(sizeof(char)*5);
    fgets(s,5,stdin);
    int n=readint(s);
    //printf("n=%d\n",n);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    //printf("s=%s\n",s2);
    const char* list[5]={"and", "not", "that", "the", "you"};
    int wsz[5]={3,3,4,3,3}; 
    int i=0,cnt=0,wl=0;
    while(cnt<n && i<N){
    //    printf("(in while loop): s=%s\n",s2+i);
        while(*(s2+i+wl)>=97 && *(s2+i+wl)<=122)wl++;
        cnt++;
    /*    printf("cnt=%d wl=%d: ",cnt,wl);
        for(int j=0;j<wl;j++){
            printf("%c",*(s2+i+j));
        }
        printf("\n");*/
        for(int k=0;k<5;k++){
            if (wl!=wsz[k])continue;
            int j=0;
            for(;j<wl;j++){
                if(*(s2+i+j)!=list[k][j]){
                    break;
                }
            }
            if(j==wl){
                printf("%s\n","Yes");
                exit(0);
            }
        }
        i+=(wl+1); wl=0;
        }
    printf("No\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
//        printf("%d\n",*(s+i));
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

このコードを書いていた時、まずは、最初の数字nの方は正常に読み込めるのだが、次の文字列の方が読み込めないという問題が生じた。この問題は、制約によりnは100以下だったため、当初は100を表すのに必要なぎりぎりの3桁分3バイトだけを fgets で読み込んでいたのを、2バイト余分に読み込んで5バイトfgetsで読むことにして解決できた。

それで、Paiza.ioで簡単な例を入力して試すとうまくいったので、AtCoderで提出したのだが、一部AC、一部WA、残りの多くはREになる。

そこで、前に質問した時にテストケースが公開されていることを教えてもらっていたので、REになっているテストケースを試したところ、実行エラーにはならないが期待した動作をしないという問題を新たに発見した。

それで、StackOverflowで質問したところ、まず、二番目の動くことは動くが想定しない結果になる問題については、比較したい文字列とリスト中の文字列をリストの要素分のループを回して比較する際、 文字数が一致していなければリストの次の要素との比較に移る部分で、continueとすべきところをbreakとしていたことが原因だったことが判明。(user20098 さんの回答による。)

REになる問題については、上記のようにfgetsでの読み込み数を3から5に修正した際に、mallocの方のchar型のバイト数のメモリの読み込み数を直し忘れていたことが原因と判明。こちらもdefineを使って一ヵ所で数字を定義していた方がよかったか。というより、最初はそうしていたのだが、二か所のfgetsの間で変数名や読み込み数の混同を生じてしまったので、nの方の読み込み数は直接数字を書くように直していたのだった。


ABC296-A

問題概要

2種類の文字を要素とする配列が与えられる。同じ文字が連続している個所があるか。

解答

n=gets.to_i
s=gets.chomp
prev=s[0]
(1...n).each do |i|
    if s[i]==prev
        puts "No"
        exit
    end
    prev=s[i]
end
puts "Yes"
    

上が本番で提出したコード。以下はCで書いたもの。

#include<stdio.h>
#include<stdlib.h>
#define N 100002
int main(void){
    int n;scanf("%d\n",&n);
    char* s=(char*)malloc(sizeof(char)*(n+2));
    fgets(s,n+1,stdin);
    for(int i=1;i<n;i++){
        if(*(s+i)==*(s+i-1)){
            printf("No");
            exit(0);
        }
    }
    printf("Yes\n");
    return 0;
}
    

ABC297-B

問題概要

K,Qを1文字、B,R,N を2文字ずつ含む8文字からなる文字列が与えられる。2つのBの一の偶奇が異なり、Kは2つのRの間にある(他の文字が間にあってもよい)という条件を満たしているか判定せよ。

解答

b1=b2=r1=r2=k=0
flag_b=flag_r=false
8.times do |i|
    if s[i]=="R"
        if flag_r==false
            r1=i
            flag_r=true
        else
            r2=i
        end
    elsif s[i]=="B"
        if flag_b==false
            b1=i
            flag_b=true
        else
            b2=i
        end
    elsif s[i]=="K"
        k=i
    end
end
if b1%2==b2%2
    puts "No"
    exit
end
if k<r1 || k>r2
    puts "No"
    exit
end
puts "Yes"
    

上が本番で提出したコード。以下はCで書いたもの。

#include<stdio.h>
#include<stdlib.h>
#define N1 10
int main(void){
    char *s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int b1=0,b2=0,r1=0,r2=0,k=0;
    int b_count=0,r_count=0,n_count=0,k_count=0,q_count=0;
    int bflag,rflag;
    bflag=rflag=0;
    for(int i=0;i<8;i++){
        if(*(s1+i)==66 && bflag==0){
            b1=i;bflag=1;b_count++;
        }else if (*(s1+i)==66 && bflag==1){
            b2=i;b_count++;
        }else if (*(s1+i)==82 && rflag==0){
            r1=i;rflag=1;r_count++;
        }else if (*(s1+i)==82 && rflag==1){
            r2=i;r_count++;
        }else if (*(s1+i)==75){
            k=i;k_count++;
        }else if (*(s1+i)==81){
            q_count++;
        }else if (*(s1+i)==78){
            n_count++;
        }
    }
    free(s1);
//    printf("b1=%d, b2=%d, r1=%d, r2=%d, k=%d\n",b1,b2,r1,r2,k);
    if (b_count!=2 || r_count!=2 || n_count!=2 || q_count!=1 || k_count!=1){
        printf("No\n");
        exit(0);
    }
    if((b1%2)^(b2%2)!=1){
        printf("No\n");
        exit(0);
    }
    if(k<r1 || k>r2){
        printf("No\n");
        exit(0);
    }
    printf("Yes\n");
    return 0;
}
s=gets.chomp
    

各文字の出現回数のチェックは不要だったことに書いた後になって気付いた。


ABC299-A

問題概要

|と|に挟まれた*があるか判定せよ。

解答

| |の中にいるかどうかをフラグを使って判定。

n=gets.to_i
s=gets.chomp
flag1=false
flag2=false
n.times do |i|
    if s[i]=="|" && flag1==false
        flag1=true
    elsif s[i]=="*" && flag1==true
        flag2=true
    elsif s[i]=="|" && flag2==true
        puts "in"
        exit
    end
end
puts "out"
    

上が本番で提出したコード。以下はCで書いたもの。

#include<stdio.h>
#include<stdlib.h>
#define N1 6
#define N 1000000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int flag1=0,found=0,i=0;
    while(*(s+i)!='\n' && *(s+i)!=0){
        if(flag1==0 && found==0 && *(s+i)=='|')flag1=1;
        else if (flag1==1 && found==0 && *(s+i)=='|'){
            printf("out\n");
            exit(0);
        }
        else if (flag1==0 && *(s+i)=='*'){
            printf("out\n");
            exit(0);
        }
        else if (flag1==1 && *(s+i)=='*'){
            found=1;
        }
        else if (flag1==1 && found==1 && *(s+i)=='|'){
            printf("in\n");
            exit(0);
        }
        i++;
    }
    printf("out\n");
    return 0;
}
    

0 件のコメント:

コメントを投稿