2025年3月10日月曜日

升目・盤面に関する問題(ABC201-250)

(ABC229-A, ABC250-A,ABC250-B)


特定の値を持つマスが辺で隣接しているか

ABC229-A

問題概要

2x2の格子が与えられる。その格子の中の全ての#が辺で隣接しているか。

解答

a=Array.new(2){Array.new(2)}
(0...2).each do |i|
    a[i]=gets.chomp.split("").to_a
end
ans="No"
n=0
(0...2).each do |i|
    (0...2).each do |j|
        if a[i][j]=="#" then
            n+=1
        end
    end
end
if n==3 or n==4 then
    ans="Yes"
end
if n==2 then
    if a[0][0]=="#" and a[1][1]=="#" then
        ans="No"
    elsif a[0][1]=="#" and a[1][0]=="#" then
        ans="No"
    else
        ans="Yes"
    end
end
puts ans    
    

上がコンテスト中に提出したコード。(2021-11-27 21:12:32)

Cで書いたのが以下。(2024-11-06 22:20:45)

#include<stdio.h>
#include<stdlib.h>
#define N1 4
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    char* s2=(char*)malloc(sizeof(char)*N1);
    if (s2==NULL){
        printf("memory allocation to sw failed.\n");
        exit(1);
    }
    char *ts2=fgets(s2,N1,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
    if(*s==35 && *s2==35 || *s==35 && *(s+1)==35 || *(s+1)==35 && *(s2+1)==35  || *s2==35 && *(s2+1)==35){
        printf("Yes\n");
        exit(0);
    }
    printf("No\n");
    return 0;
}
    

各マスが#か.かどうかで、4マスでは2^4=16通りのパターンが考えられるが、そのうち、全ての#が辺で隣接しているのは、((0,0),(0,1)), ((0,0),(1,0), ((1,0),(1,1)), ((0,1),(1,1)) が#の場合で、それが満たされていれば他のマスは何でもよい。すなわち、

## #? ?? ?#
?? #? ## ?#
の場合である。よって、この4通りの条件のいずれかが満たされているかどうかを調べればよい。

3年前にRubyで書いたときのコードを見てみたら、4隅のマスのうち対角線上の2ヶ所がともに.であれば条件を満たさないとしていた。確かにそう考えてもいい。同じ自分でも時が経つと違う発想をするものだと改めて思う。


辺で隣接する升目の数

ABC250-A

問題概要

h行w列の升目が与えらられる。マス(r,c)に辺で隣接するマスの個数を求めよ。

解答

コンテスト中にはまず以下のコードを書いたのだが、このコードでは15のテストケースのうち5つでWAになった。(2022-05-08 21:20:48)

h,w=gets.chomp.split(" ").to_a.map(&:to_i)
r,c=gets.chomp.split(" ").to_a.map(&:to_i)
ans=4
if (r==1 || r==h) && (c==1 || c==w)
    ans=2
elsif (r==1 || r==h) || (c==1 || c==w)
    ans=3
end
puts ans
    

このコードは、以下のように考えて書いた。

  • まず、原則として、1つのマスに辺で接する他の辺は上下左右の4つである。
  • 例外として、まず、マスが4つの隅のいずれかにあるとき、接するマスは2つになる。
  • また、マスが隅ではないが盤面の辺に接しているとき、接するマスは3つになる。

上のコードでは、盤面のサイズが3x3以上であることを前提としているが、制約ではh,wは1以上10以下であるから、縦または横の長さが1である場合も考慮しなければならない。それで書き直して提出したのが以下のコードだが、こちらもWAとなった。(2022-05-08 21:20:48)

h,w=gets.chomp.split(" ").to_a.map(&:to_i)
r,c=gets.chomp.split(" ").to_a.map(&:to_i)
ans=4
if h==1 && w==1
    ans=0
elsif (h==1) && (c==1 || c==w)
    ans=1
elsif h==1
    ans=2
elsif (w==1) && (r=1 || r=h)
    ans=1
elsif w==1
    ans=2
elsif (r==1 || r==h) && (c==1 || c==w)
    ans=2
elsif (r==1 || r==h) || (c==1 || c==w)
    ans=3
end
puts ans
    

このコードでは、以下のように考えている。

  • まず、盤面のサイズが1x1の場合、隣接するマスはないので、答えは0。
  • 盤面の縦または横の長さの一方のみが1であり、他方は1でない場合、(i) マス(r,c)が長さが1でない方向の端にあれば(具体的には、縦の長さが1であるときは左端か右端、横の長さが1であるときは上端か下端にあれば)、接しているマスは1個。(ⅱ)端になければ、接しているマスは左右または上下の2箇所。
  • それ以外の場合、最初に書いたコードと同じ。

    結果は、1つのテストケース(test_05.txt)でWAとなった。これでも結構試行錯誤した末に行きついたもので、A問題にしては難しいと感じた。結局、コンテスト中に正解することができなかった。

    今(2025年2月)改めて考えてみても、何が原因か分からない。しかも、間が悪いことに前年の暮れからAtCoderのテストケースが非公開となっており、もはやテストケースを見て原因を探ることができない。

    諦めかけたところでもう一度このコードをよく見てみると、二番目の条件式で、== とすべきところが = となっている。そこだけ直して提出したのが以下のコード。(2025-02-26 22:00:38)これでACになった。単純な書き間違いだったのに気付くのに3年弱かかってしまった。

    h,w=gets.chomp.split(" ").to_a.map(&:to_i)
    r,c=gets.chomp.split(" ").to_a.map(&:to_i)
    ans=4
    if h==1 && w==1
        ans=0
    elsif (h==1) && (c==1 || c==w)
        ans=1
    elsif h==1
        ans=2
    elsif (w==1) && (r==1 || r==h)
        ans=1
    elsif w==1
        ans=2
    elsif (r==1 || r==h) && (c==1 || c==w)
        ans=2
    elsif (r==1 || r==h) || (c==1 || c==w)
        ans=3
    end
    puts ans
        

    なお、解説を見ると、以下のような方法が示してあった。

    1. (r,c)が1行目でなければ、上のマスと接しているので、1を加算
    2. (r,c)がn行目でなければ、下のマスと接しているので、1を加算
    3. (r,c)が1列目でなければ、左のマスと接しているので、1を加算
    4. (r,c)がn列目でなければ、右のマスと接しているので、1を加算

    これでやってみたのが次のコード。(2025-02-26 22:24:03)

    h,w=gets.chomp.split(" ").to_a.map(&:to_i)
    r,c=gets.chomp.split(" ").to_a.map(&:to_i)
    ans=0
    if r!=1
        ans+=1
    end
    if r!=h
        ans+=1
    end
    if c!=1
        ans+=1
    end
    if c!=w
        ans+=1
    end
    puts ans
        

    Cで書いたときには、次のように考えて解いてみた。まず、与えられた盤面の大きさを一回り大きくした、(h+2)*(w+2)の大きさの盤面を作り、全てのマスの数字を0にする。続いて、盤面の一番外側のマスを除いたh*w個のマスの数字を1にする。そして、座標(r,c)の上下左右のマスの数字を足し合わせる。これで、もとの盤面上でマス(r,c)に隣接するマスの数が求められるはずである。それで書いたのが以下のコード。結果は、15件のテストケース中、4件のテストケースでREとなった。(2025-02-26 22:46:13)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int h=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int w=readint(s+i);
        free(s);
        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);
    //    printf("%s\n",s2);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int r=readint(s2);
        i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int c=readint(s2+i);
        free(s2);
    //    printf("h=%d w=%d r=%d c=%d\n",h,w,r,c);
        char ans=0;
        char* board=(char*)malloc(sizeof(char)*(h+2)*(r+2));
        int j;
        for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                *(board+(w+2)*i+j)=0;
            }
        }
        for(i=1;i<=h;i++){
            for(j=1;j<=w;j++){
                *(board+(w+2)*i+j)=1;
            }
        }
    /*    for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                printf("%d",*(board+(w+2)*i+j));
            }
            printf("\n");
        }*/
        ans+=*(board+(w+2)*(r-1)+(c));
        ans+=*(board+(w+2)*(r)+(c-1));
        ans+=*(board+(w+2)*(r)+(c+1));
        ans+=*(board+(w+2)*(r+1)+(c));
        printf("%d\n",ans);
        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;
    }
        

    メモリの範囲外アクセスを疑うが、どこでそれが起きているのか分からない。サンプルのケースでは全てACになっている。テストケースの公開が停止されていることを恨むが、幸いというべきか、この問題では四つの変数がともに10以下なので、全ての組み合わせをしらみつぶしに試しても10^4通りだ。とりあえず、適当な数字を幾つか打ち込んで試してみる。

    予想していたよりも早く、h=10,w=10,r=1,c=1 とした時点で実行エラーになった。h=9,w=9 としても同じだ。

    ここで、原因は先の3年越しの解決となったrubyのコードと同じ、単純な書き間違いではないかと思い至る。そうして探してみると、見事に、wとすべきところをrとしている所が見つかった。

    そこを直して提出したのが以下のコード。これで全てACになった。(2025-03-03 21:21:25)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 7
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int h=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int w=readint(s+i);
        free(s);
        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);
    //    printf("%s\n",s2);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int r=readint(s2);
        i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int c=readint(s2+i);
        free(s2);
    //    printf("h=%d w=%d r=%d c=%d\n",h,w,r,c);
        char ans=0;
        char* board=(char*)malloc(sizeof(char)*(h+2)*(w+2));
        int j;
        for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                *(board+(w+2)*i+j)=0;
            }
        }
        for(i=1;i<=h;i++){
            for(j=1;j<=w;j++){
                *(board+(w+2)*i+j)=1;
            }
        }
    /*    for(i=0;i<h+2;i++){
            for(j=0;j<w+2;j++){
                printf("%d",*(board+(w+2)*i+j));
            }
            printf("\n");
        }*/
        ans+=*(board+(w+2)*(r-1)+(c));
        ans+=*(board+(w+2)*(r)+(c-1));
        ans+=*(board+(w+2)*(r)+(c+1));
        ans+=*(board+(w+2)*(r+1)+(c));
        printf("%d\n",ans);
        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;
    }
        

    長方形タイルの市松模様

    ABC250-B

    問題概要

    正方形を縦にa個、横にb個並べて一枚としたタイルがある。このタイルを縦横それぞれn枚並べる。各タイルは、#または.に塗られている。左上のタイルの色を.として、このタイル全体からなる模様を出力せよ。

    解答

    def toggle_flag(flag)
        if $flag==false
            $flag=true
        else
                $flag=false
        end
    end
    n,a,b=gets.chomp.split(" ").to_a.map(&:to_i)
    $flag=false
    n.times do |i|
        str=""
        n.times do |j|
            if $flag==true
                c="#"
            else
                c="."
            end
            str+=c*b
            toggle_flag($flag)
        end
        a.times do |k|
            puts str
        end
        if n%2==0
            toggle_flag($flag)
        end
    end            
        

    上がコンテスト中に提出したコード。よく見るとtoggle_flag関数の引数に意味がなかったが、まあいい。

    次に塗るタイルの色が黒か白かを表すフラグを用意する。フラグがtrueなら黒、falseなら白とする。右上のタイルは白なので、初めはフラグはfalseである。

    各行の出力用の空文字列を用意し、タイルの横幅分の#か.をまとめて連結。そして連結するごとにフラグを切り替える。こうして一行分の文字列が完成したら、それをタイルの縦の長さ分出力。

    続いて、次の行ブロックのタイルの出力に入りたいのだが、その前に、nの偶奇によって処理を分ける必要がある。すなわち、nが偶数であれば、ある行ブロックの最後の列のタイルの色と、次の行ブロックの最初の列のタイルの色は同じなのだが、奇数の場合には、異なる色となる。.なので、nが偶数の場合には、フラグの切り替えを行う。(最後の列のタイルを連結した後にいったんフラグを変えているので、もう一度フラグを切り替えることで、前の行ブロックの最後の列のタイルと同じ色にしている。)

    以上の処理をn回繰り返せばよい。

    ところで、市松模様では、行番号と列番号の偶奇が同じ場合と異なる場合で色が分かれる。つまり、(行番号、列番号)が(偶数、偶数)の場合と(奇数、奇数)の場合が一つの色となり、(偶数、奇数)の場合と(奇数、偶数)の場合が他方の色となる。これは、行番号+列番号が偶数になる場合(偶数+偶数、奇数+奇数は偶数)と奇数になる場合(偶数+奇数は奇数)とで色が異なると表現することもできる。

    だが、その性質を本問のように一文字ずつ市松模様にするのではない場合にどのように使ったらいいのか分からなかったので、コンテスト中には上記のような解法をとった。

    解説を見てみてみると、まず、タイルの色を格納したn*n行の行列Aを作り、それをi行j列を塗る際にA[i/a][j/b]の形で参照することで、その座標のタイルの色を得ている。一文字単位では縦がa*n行、横がb*n行なので、i,jをa,bで割った商が、ちょうどその場所の色を示す行列の行数と列数になるのだ。

    それでやってみたのが以下のコード。

    n,a,b=gets.chomp.split(" ").to_a.map(&:to_i)
    tiles=Array.new(n){Array.new(n,"")}
    n.times do |i|
        n.times do |j|
            if (i+j)%2==0
                tiles[i][j]="."
        else
                tiles[i][j]="#"
            end
        end
    end
    board=Array.new(n*a){Array.new(n*b,"")}
    (n*a).times do |i|
        (n*b).times do |j|
            board[i][j]=tiles[i/a][j/b]
        end
        puts board[i].join("")
    end
        

    Cで書いたとき、まずはrubyで最初に書いたときと同じように、b文字出力するごとに文字を切り替えるやり方でやってみた。それが以下のコード。(2025-03-05 21:29:25)結果は、18件のテストケース中5件でWAとなった。

    int readint(char *s);
    char color=46;
    void toggle(void){
        if(color==46)color=35;
        else if(color==35)color=46;
    }
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j,k,l;
        for(i=0;i<n;i++){
            for(j=0;j<a;j++){
                for(k=0;k<n;k++){
                    for(l=0;l<b;l++){
                        printf("%c",color);
                    }
                    toggle();
                }
                printf("\n");
            }
            if(n%2==0)toggle();
        }
        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;
    }
        

    幸いにも、今回はサンプル中でも1件でWAとなっているので、そこを手掛かりにすることができる。そのテストケースでは、
    1 4 4 という入力例で、

    ....
    ....
    ....
    ....
    
    となるべき出力結果が
    ....
    ####
    ....
    ####
    
    となっている。

    何がいけなかったのかを考えてみると、上のコードはRubyで最初に書いたと基本的には同じやり方であるものの、一ヵ所違いがあることに気づく。Rubyで書いたコードでは、上の方法(b文字出力するごとに#と.を切り替える)で1行分の文字列を作った後、それをa回出力しているのに対し、今回Cで書いたコードでは、「b文字出力するごとに#と.を切り替えることをn回繰り返したのち、改行する」ことをa回繰り返しているのである。そのため、nが奇数のとき、本来ならば1つのタイル内では行の最後のマスと次の行の最初のマスが同じ色でなければならないのに、行の最後で色を切り替えたまま次の行に入ってしまい、次の行の最初のマスが別の色になってしまっているのである。

    そこを修正するために、一行の処理を終えた後、nが奇数ならばもう一度文字を切り替える処理を入れる。そして、これに伴って、Rubyで書いたコードでの、「nが偶数のとき、行方向にタイルが変わるごとに色を切り替える」処理の代わりに、「行方向にタイルが変わるごとに、常に色を切り替える」処理をすることになる。なぜなら、行の最後のb文字の出力を終えた後に一度文字の切り替えを行い、nが奇数のときだけ各行ごとに文字の切り替えを行い、タイルが変わる行に入る時にまた文字を切り替えることで、タイル行が変わる際、nが偶数ならば2回、nが奇数ならば3回の文字の文字の切り替えが行われることになる。であるから結局、nが偶数のときには、2回の切り替えが行われた結果色は変わらないことになり、新しいタイル行の最初のマスは前のタイル行の最後のマスと同じ色になるからである。

    それが以下のコード。(2025-03-05 21:49:41 )

    int readint(char *s);
    char color=46;
    void toggle(void){
        if(color==46)color=35;
        else if(color==35)color=46;
    }
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j,k,l;
        for(i=0;i<n;i++){
            for(j=0;j<a;j++){
                for(k=0;k<n;k++){
                    for(l=0;l<b;l++){
                        printf("%c",color);
                    }
                    toggle();
                }
                if(n%2==1)toggle();
                printf("\n");
            }
            toggle();
        }
        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;
    }
        

    Cで書いたもう一つのコードは、コンテスト中にRubyで書いたときには考えながらも実装できなかった、「市松模様では、「行番号+列番号」の偶奇と色が対応している」という性質を使ったもの。これに、a*n行、b*n列の行、列の番号をそれぞれ a,b で割ったものが、各タイルを1つのマスとしたときの行番号、列番号になる」ということを組み合わせると、以下のようなコードになる。(2025-03-05 22:09:42)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 10
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        int i=0;
        while(*(s+i)!=32)i++;
        i++;
        int a=readint(s+i);
        while(*(s+i)!=32)i++;
        i++;
        int b=readint(s+i);
        free(s);
    //    printf("n=%d a=%d b=%d\n",n,a,b);
        int j;
        for(i=0;i<n*a;i++){
            for(j=0;j<n*b;j++){
                if((i/a+j/b)%2==0)printf("%c",46);
                else printf("%c",35);
            }
            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;
    }
        

    2025年3月5日水曜日

    文字列に関する問題(ABC201-ABC250)

    (ABC202-B, ABC211-B, ABC215-A, ABC217-A, ABC217-B, ABC218-A, ABC219-B, ABC221-B, ABC223-B, ABC224-A, ABC230-B, ABC232-B, ABC233-B, ABC236-A, ABC242-B, ABC244-A, ABC248-A)


    逆順への並び替えと置換

    ABC202-B

    問題概要

    0,1,6,8,9からなる文字列が与えられる。この文字列を逆順に並べたうえ、6は9に、9は6に変換して出力せよ。

    解答

    s=gets.chomp
    n=s.size
    n.times do |i|
        c=s[n-1-i]
        if c=="6"
            c="9"
        elsif c=="9"
            c="6"
        end
        print c
    end
    print "\n"
        

    この時は本番不参加。上のrubyのコード(2024-04-12 22:58:53)と下のCのコード(2024-04-12 23:09:05 )は同時に書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1e5+2
    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 i=0;
        while(*(s1+i)>=48 && *(s1+i)<=57)i++;
        i--;
        while(i>=0){
            char c=*(s1+i);
            if(c==54)c=57;
            else if(c==57)c=54;
            printf("%c",c);
            i--;
        }
        printf("\n");
        return 0;
    }
        

    文字列のリストが一致するか。

    ABC211-B

    問題概要

    4つの文字列が与えられる。これが、H,2B,3B,HR それぞれ1つずつか判定せよ。

    解答

    #n=gets.chomp.to_i
    s=[]
    (0..3).each do |i|
        s[i]=gets.chomp
    end
    #n,l=gets.chomp.split(" ").map(&:to_i)
    ans=""
    s.sort!
    str=["2B","3B","H","HR"]
    
    if s==str then
        ans="Yes"
    else
        ans="No"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-07-24 21:12:33)

    Cで書いたコードが以下。(2024-05-22 23:19:17)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 4
    int main(void){
        int *ref=(int*)malloc(sizeof(int)*4);
        for(int i=0;i<4;i++)*(ref+i)=0;
        for(int i=0;i<4;i++){
            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);
            }
    //        printf("%s",s1);
            if(*s1==50 && *(s1+1)==66)*(ref+1)=1;
            else if(*s1==51 && *(s1+1)==66)*(ref+2)=1;
            else if(*s1==72 && *(s1+1)==82)*(ref+3)=1;
            else if(*s1==72)*ref=1;
        }
    //    for(int i=0;i<4;i++)printf("%d ",*(ref+i));
        int flag=1;
        for(int i=0;i<4;i++){
            if(*(ref+i)==0){
                flag=0;
                break;
            }
        }
    //    printf("flag=%d\n",flag);
        if(flag==1){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
        

    rubyで書いたときは、与えられた四つの文字列からなる配列をソートし、それが 2B,3B,H,HRという配列と一致するかどうかで判定したが、Cで書いたときには出現すべき文字列が全て出てきているかで判定している。


    文字列の一致判定

    ABC215-A

    問題概要

    与えられた文字列が Hello,World! と完全に一致するか。

    解答

    #n=gets.chomp.to_i
    s=gets.chomp
    ans="WA"
    if s.eql?("Hello,World!") then
        ans="AC"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-08-21 21:04:08)

    Cで書いたのが以下。(2024-06-12 22:59:37)

    int slen2(const char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 17
    #define N2 12
    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 hello[]="Hello,World!";
        int size=slen2(s1);
    /*    printf("s=%s\n",s1);
        printf("size of s=%d\n",size);
        printf("hello=%s\n",hello);
        printf("size of hello=%d\n",slen2(hello));*/
        if(size!=N2){
            printf("WA\n");
            exit(0);
        }
        for(int i=0;i<N2;i++){
            if(*(s1+i)!=*(hello+i)){
                printf("WA\n");
                exit(0);
            }
        }
        printf("AC\n");
        return 0;
    }
    int slen2(const char *s){
        int i=0;
        while((*(s+i)>=97 && *(s+i)<=122) || (*(s+i)>=65 && *(s+i)<=90) || *(s+i)==44 || *(s+i)==33)i++;
        return i;
    }
        


    文字列の辞書順比較

    ABC217-A

    問題概要

    文字列sは文字列tよりも辞書順で前に来るか。

    解答

    s,t=gets.chomp.split(" ")
    ans="No"
    if s<t then
        ans="Yes"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-04 21:07:25)

    Cで書いたのが以下。(2024-10-04 22:59:07)

    int scmp(char* s, char* t);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 23
    #define N2 11
    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 i=0;
        while(*(s1+i)>=97 && *(s1+i)<=122){
            i++;
        }
        *(s1+i)=0;
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i++;
        int j=0;
        while(*(s1+i)>=97 && *(s1+i)<=122){
            *(s2+j)=*(s1+i);
            i++; j++;
        }
        *(s2+j)=0;
    //    printf("s1=%s s2=%s\n",s1,s2);
        if(scmp(s1,s2)<0){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
        return 0;
    }
    int scmp(char* s, char* t){
        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++;
        }
    }
        

    含まれていない文字列

    ABC217-B

    問題概要

    3つの文字列が与えられる。ABC,ARC,AGC,AHCのうち、与えられた文字列にないものを出力せよ。

    解答

    a=["ABC","ARC","AGC","AHC"]
    b=[]
    3.times do
        t=gets.chomp
        b.push(t)
    end
    puts (a-b)[0]
        

    上がコンテスト中に提出したコード。(2021-09-04 21:11:56)

    Cで書いたのが以下。(2024-10-04 23:23:38)

            int scmp(char* s, char* t);
            #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 i=0;
                while(*(s1+i)>=65 && *(s1+i)<=90){
                    i++;
                }
                *(s1+i)=0;
                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);
                }
                i=0;
                while(*(s2+i)>=65 && *(s2+i)<=90){
                    i++;
                }
                *(s2+i)=0;
                char* s3=(char*)malloc(sizeof(char)*N1);
                if (s3==NULL){
                    printf("memory allocation to s3 failed.\n");
                    exit(1);
                }
                char *ts3=fgets(s3,N1,stdin);
                if(ts3==NULL){
                    printf("fgets(s3) failed\n");
                    exit(1);
                }
                i=0;
                while(*(s3+i)>=65 && *(s3+i)<=90){
                    i++;
                }
                *(s3+i)=0;
                char* abc="ABC";
                char* arc="ARC";
                char* agc="AGC";
                char* ahc="AHC";
                int a[4]={0};
                if(scmp(s1,abc)==0 || scmp(s2,abc)==0 || scmp(s3,abc)==0){
                    a[0]=1;
                }
                if(scmp(s1,arc)==0 || scmp(s2,arc)==0 || scmp(s3,arc)==0){
                    a[1]=1;
                }
                if(scmp(s1,agc)==0 || scmp(s2,agc)==0 || scmp(s3,agc)==0){
                    a[2]=1;
                }
                if(scmp(s1,ahc)==0 || scmp(s2,ahc)==0 || scmp(s3,ahc)==0){
                    a[3]=1;
                }
            /*    printf("s1=%s s2=%s s3=%s\n",s1,s2,s3);
                for(int i=0;i<4;i++){
                    printf("%d ",*(a+i));
                }*/
                if(a[0]==0){
                    printf("ABC\n");
                }else if(a[1]==0){
                    printf("ARC\n");
                }else if(a[2]==0){
                    printf("AGC\n");
                }else if(a[3]==0){
                    printf("AHC\n");
                }
                return 0;
            }
            int scmp(char* s, char* t){
                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++;
                }
            }
            
        

    文字列のi番目の文字

    ABC218-A

    問題概要

    oとxとからなる文字列と整数nが与えられる。文字列のn番目の文字はoか。

    解答

    n=gets.chomp.to_i
    s=gets.chomp.split("").to_a
    ans="No"
    if s[n-1]=="o" then
        ans="Yes"
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-11 21:05:58)

    Cで書いたのが以下。(2024-10-07 22:24:56)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 3
    #define N2 9
    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);
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        if(*(s2+n-1)==111){
            printf("Yes\n");
        }else{
            printf("No\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;
    }
        

    文字列の連結

    ABC219-B

    問題概要

    3つの文字列s[1],s[2],s[3]と、1,2,3を要素とする整数列{a_n}が与えられる。整数列の順番にs[a[i]]を連結した文字列を出力せよ。

    解答

    s1=gets.chomp
    s2=gets.chomp
    s3=gets.chomp
    t=gets.chomp.split("").map(&:to_i)
    n=t.size
    ans=""
    n.times do |i|
        if t[i]==1 then
            ans+=s1
        elsif t[i]==2 then
            ans+=s2
        elsif t[i]==3 then
            ans+=s3
        end
    end
    puts ans
        

    上がコンテスト中に提出したコード。(2021-09-18 21:13:37)

    Cで書いたのが以下。(2024-10-09 23:04:12)

    int readint(char *s);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 3
    #define N2 10
    #define N3 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*((N2+1)*N1));
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        int idx=0;
        for(int i=0;i<N1;i++){
            char* s1=(char*)malloc(sizeof(char)*(N2+2));
            if (s1==NULL){
                printf("memory allocation to s1 failed.\n");
                exit(1);
            }
            char *ts1=fgets(s1,N2+2,stdin);
            if(ts1==NULL){
                printf("fgets(s1) failed\n");
                exit(1);
            }
    //        printf("s1=%s",s1);
            idx=(N2+1)*i;
            int j=0;
            while(*(s1+j)>=97 && *(s1+j)<=122){
                *(s+idx+j)=*(s1+j); j++;
            }
    //        *(s+idx+j)=0;printf("j=%d\n",j);
            free(s1);
        }
    /*    for(int i=0;i<3;i++){
            printf("%s\n",s+i*(N2+1));
        }*/
        char* s3=(char*)malloc(sizeof(char)*N3);
        if (s3==NULL){
            printf("memory allocation to s3 failed.\n");
            exit(1);
        }
        char *ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(s3) failed\n");
            exit(1);
        }
        int k=0;
        while(*(s3+k)>=49 && *(s3+k)<=51){
            printf("%s",(s+(*(s3+k)-49)*11)); k++;
        }
        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;
    }
        

    ABC221-B

    問題概要

    文字列sの隣り合う2文字を選び入れ替えることを高々1回行うことで、sをtと一致させることができるか。

    解答

    s=gets.chomp.split("").to_a
    t=gets.chomp.split("").to_a
    a=t.size
    #b=(0...a).to_a.combination(2).to_a
    #sz=a*(a-1)/2
    def swap(x,y)
        x,y=y,x
    end
    if s==t then
        puts "Yes"
        exit
    end
    (0...a-1).each do |i|
        #t[b[i][0]],t[b[i][1]]=t[b[i][1]],t[b[i][0]]
        t[i],t[i+1]=t[i+1],t[i]
        if s==t then
            puts "Yes"
            exit
        else
            #t[b[i][0]],t[b[i][1]]=t[b[i][1]],t[b[i][0]]
            t[i],t[i+1]=t[i+1],t[i]
        end
    end
    puts "No"
        

    上がコンテスト中に提出したコード。(2021-10-02 21:39:41)

    Cで書いたのが以下。(2024-10-16 22:53:12)

    int scmp1(char* s, char* t, int n);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 102
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* t=(char*)malloc(sizeof(char)*N1);
        if (t==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(t,N1,stdin);
        if(ts2==NULL){
            printf("fgets(t) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122){
            i++;
        }
        *(s+i)=0;
        *(t+i)=0;
        int n=i;
        if(scmp1(s,t,n)==0){
            printf("Yes\n");
            exit(0);
        }
        i=1;
        for(;i<n;i++){
            char temp=*(s+i-1);
            *(s+i-1)=*(s+i);
            *(s+i)=temp;
            if(scmp1(s,t,n)==0){
                printf("Yes\n");
                exit(0);
            }
            *(s+i)=*(s+i-1);
            *(s+i-1)=temp;
            
        }
        printf("No\n");
        return 0;
    }
    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 (*(t+i)-*(s+i));
            else i++;
            if(i>n){
                printf("s=%s, t=%s, i=%d\n",s,t,i);
                printf("Comparing failed.\n");
            }
        }
    }
        

    辞書順で最大と最小

    ABC223-B

    問題概要

    与えられた文字列に対し、「先頭の文字を末尾に移動する操作」ないしは「末尾の文字を先頭に移動する操作」を任意の回数行って得られる文字列のうち、辞書順で最小のものと最大のものを求めよ。

    解答

    #n,p=gets.chomp.split(" ").map(&:to_i)
    #a=gets.chomp.split(" ").map(&:to_i)
    s=gets.chomp
    maxs=s
    mins=s
    sa=s.split("").to_a
    sz=s.size
    t=[]
    ts=""
    (0...sz).each do |i|
        temp=t[sz-1]
        (0...sz).each do |j|
            if j-i>=0 then
                t[j]=sa[j-i]
            else
                t[j]=sa[j+sz-i]
            end
        end
        ts=t.join("")
        if ts>maxs then
            maxs=ts
        end
        if ts<mins then
            mins=ts
        end
    end
    puts mins
    puts maxs
        

    上がコンテスト中に提出したコード。(2021-10-17 21:26:51)

    Cで書いたコードについて、まず誤ったコードを以下に示す。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 6
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
    //    printf("s=%s\n",s);
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
    //    printf("sd=%s\n",sd);
        char min=122, max=97;
        int min_pos=0, max_pos=0;
        for(i=0;i<sz;i++){
            if(*(s+i)<min){
                min=*(s+i);
                min_pos=i;
            }
            if(*(s+i)>max){
                max=*(s+i);
                max_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+(i%sz)));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+(i%sz)));
            }
        printf("\n");
        return 0;
    }
        

    右シフトを行っても左シフトを行っても、得られるのは元の文字列から作った環状配列の一部である。だから、s[min]の場合には最初の文字が最も辞書順で小さいものとなるように環状配列の開始位置を選べばよいのではないかと考えて書いたのがこのコードである。しかし、このアルゴリズムでは、例えばaabaとaaabの大小を正しく把握できず、最初に見つかった前者を誤って最小の文字列としてしまうことが分かった。

    とすると、やはり文字列全体を見て大小を比較するしかない。しかし、毎回文字列の大小比較をするのは効率が悪いように思われるし、文字列の大小比較用に作った自作関数のscmpは文字列の最後にヌル文字があることを前提に作っている。(これは、組み込みのstrcmp関数でも同じはず。)そこで、今回は、文字列を26進数の数値とみなし、それを10進法に直した数値どうしで大小を比較することにした。文字列どうしを直接比較する場合、早ければ最初の文字どうしで大小が決まってしまうのに対し、この変換を行う場合には文字列を最後まで読まなければならないから、今回の問題に関してはこちらの方が計算量が多くなってしまうかの感はあるのだが、一度数値に直してしまえばあとは整数型どうしの比較で済むので、同じ文字列を何回も比較にかけなければならないソートなどの場合にはこちらの方が計算量を少なくできるのではないか。そう思って書いたのが以下。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 6
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
        int min=456975, max=0;
        int min_pos=0, max_pos=0;
        for(int i=0;i<sz;i++){
            int k=0;
            for(int j=0;j<sz;j++){
                k*=26;
                k+=*(sd+i+j)-97;
            }
            if(k>max){
                max=k;
                max_pos=i;
            }
            if(k<min){
                min=k;
                min_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+i));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+i));
        }
        printf("\n");
        return 0;
    }
        

    このコードは、桁が小さい場合には正しい答えが出る(はず)。しかし、一部を除くテストケースではWAになっている。なにが問題だったかというと、制約の、sの長さは1000以下というのを、26進法で4桁以下(つまり、最大でzzzz。即ち10進法の26^4-1=456975)と読み誤ってしまったことだ。しかし、サンプル(3つのうち1つがWAになっている。)を見直してみると、4桁を超えるものもある。実際には、26進法で1000桁ということだったのだ。そう読むのが問題文の素直な読み方なのだが、1000桁の数字など出るはずがないという思い込みが、上のような読み方をさせてしまったらしい。log10(26^1000)=1000*log10(26)≓1000*1.414973≓1414.9だから、これは1415桁になる。unsigned long long でも18桁までしか扱えないから、C言語の整数型で扱える範囲を遥かに超えた数になってしまう。ちなみに、RのコンソールやGoogle電卓で26^1000とか叩いてみても答えは返ってこなかった。ということは、文字列を26進法の数値とみなし10進法に直して比較するというやり方には無理があったのだ。

    すると、やはり文字列のまま比較するしかない。比較する2つの文字列の文字数は同じだから、ヌル文字がなくても、桁数分まで行ったところで比較を打ち切るようにscmp関数を書き換えれば、文字列の大小比較は可能だ。そうして、改めてCで書いたてACになったのが以下。(2024-10-23 23:35:59)

    int scmp_n(char* s, char* t, int n);
    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
    //    printf("s=%s\n",s);
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        char* sd=(char*)malloc(sizeof(char)*sz*2);
        if (sd==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        i=0;
        for(;i<sz*2;i++){
            *(sd+i)=*(s+(i%sz));
        }
        int min_pos=0, max_pos=0;
        for(i=1;i<sz;i++){
            if(scmp_n(sd+i,sd+min_pos,sz)>0){
                min_pos=i;
            }
            if(scmp_n(sd+i,sd+max_pos,sz)<0){
                max_pos=i;
            }
        }
        for(i=0;i<sz;i++){
            printf("%c",*(sd+min_pos+i));
        }
        printf("\n");
        for(i=0;i<sz;i++){
            printf("%c",*(sd+max_pos+i));
            }
        printf("\n");
        return 0;
    }
    int scmp_n(char* s, char* t, int n){
        int i=0;
        while(i<n){
            if (i==n-1 && *(s+i)==*(t+i)) return 0;
            else if (*(t+i)!=*(s+i)) return (*(t+i)-*(s+i));
            else i++;
        }
    }
        

    文字列についての条件判定

    ABC224-A

    問題概要

    末尾が er または ist であるような文字列 S が与えられます。S の末尾が er である場合は er を、 ist である場合は ist を出力してください。

    解答

    s=gets.chomp.split("").to_a
    sz=s.size
    #a=gets.chomp.split(" ").map(&:to_i)
    ans="er"
    if s[sz-1]=="t" then
        ans="ist"
    end
    puts ans
            

    上がコンテスト中に提出したコード。(2021-10-23 21:02:29)

    Cで書いたのが以下。(2024-10-21 22:53:48)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 22
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int i=0;
        while(*(s+i)>=97 && *(s+i)<=122)i++;
        int sz=i;
        if(*(s+sz-1)==114 && *(s+sz-2)==101){
            printf("%c%c\n",101,114);
        }else if (*(s+sz-1)==116 && *(s+sz-2)==115 && *(s+sz-3)==105){
            printf("%c%c%c\n",105,115,116);
        }
        return 0;
    }
        

    ABC230-B

    問題概要

    与えられた文字列は、oxx を 10^5 個結合した文字列の部分文字列か。

    解答

    n=gets.chomp.to_i
    if n>=42 then
        n+=1
    end
    printf("AGC%03d\n",n)
    

    上がコンテスト中に提出したコード。(2021-12-03 21:14:30)

    Cで書いたのが以下。(2024-11-12 00:11:16)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 12
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int sz=0;
        while(*(s+sz)==111 || *(s+sz)==120)sz++;
        char* t=(char*)malloc(sizeof(char)*12);
        if (t==NULL){
            printf("memory allocation to t failed.\n");
            exit(1);
        }
        char ss[]={111,120,120};
        for(int i=0;i<4;i++){
            for(int j=0;j<3;j++){
                *(t+i*3+j)=*(ss+j);
            }
        }
        for(int i=0;i<3;i++){
            int flag=1;
            for(int j=0;j<sz;j++){
                if(*(t+i+j)!=*(s+j)){
                    flag=0;
                    break;
                }
            }
            if(flag==1){
                printf("Yes\n");
                exit(0);
            }
        }
        printf("No\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;
    }
        

    Rubyで書いたときには馬鹿正直にoxxを10^5個繋げた文字列を作って、sがその部分文字列かどうかを調べたのだが、sは10文字以下という制約があるのだから、oxxを4個繋げた、全部で12文字の文字列を作り、その1~(sの文字数)文字目、2~(sの文字数+1)文字目、3~(sの文字数+2)文字目のいずれかと一致するかどうかを調べれば、sが最大の10文字の場合でも在り得る全パターンを調べることが出する。Cで書いたときにはそのようにして書いた。

    なお、最初は char* ss={111,120,120}; と書いたのだが、コンパイルエラーになった。そういえば、cではただポインタ宣言をするだけではメモリは確保されないからこうは書けないのだった。char ss[]={111,120,120}; と書き直して無事動いた。


    カエサル暗号

    ABC232-B

    問題概要

    文字列sをカエサル暗号により暗号化する(文字列sの全て文字を、英アルファベット26文字の環状配列でk後ろにある文字に置き換える)ことで、文字列tに一致させることができるか。

    解答

    a=gets.chomp.bytes.to_a
    b=gets.chomp.bytes.to_a
    c=[]
    a.size.times do |i|
        c[i]=a[i]-b[i]
        if c[i]<0 then
            c[i]+=26
        end
    end
    t=c[0]
    flag=true
    a.size.times do |i|
        if c[i]!=t then
            flag=false
        end
    end
    if flag==true then
        puts "Yes"
    else
        puts "No"
    end    

    上がコンテスト中に提出したコード。(2021-12-19 21:37:49)

    Cで書いたのが以下。(2024-11-20 22:36:27)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 1e5+2
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* t=(char*)malloc(sizeof(char)*N1);
        if (t==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts2=fgets(t,N1,stdin);
        if(ts2==NULL){
            printf("fgets(t) failed\n");
            exit(1);
        }
        int sz=0;
        while(*(s+sz)>=97 && *(s+sz)<=122)sz++;
        int k=(*s-*t+26)%26;
        for(int i=1;i<sz;i++){
            if((*(s+i)-*(t+i)+26)%26!=k){
                printf("No");
                exit(0);
            }
        }
        printf("Yes");
        return 0;
    }
        

    カエサル暗号で一致するということは、基準となる文字からのずれが一致するということである。そう考えて今回Cでのコードを書いたが、その後でRubyで書いたときのコードを見てみると、多少の違いはあるものの同じ考え方で解いていた。多分、こういう場合にまず思いつくのは、片方の文字列の各文字を1つずつ後ろにずらした文字列を生成しつつ、それが他方の文字列と一致するかどうかを最大で26通り試すというものではないかと思うのだが、三年前の自分もそう単純ではなかったようだ。


    文字列の反転

    ABC233-B

    問題概要

    与えられた文字列のl文字目からr文字目までを反転した文字列を出力せよ。

    解答

    l,r=gets.split.map(&:to_i)
    s=gets.chomp.chars
    n=s.size
    s1=s.slice(0,l-1)
    s2=s.slice(l-1,r-l+1)
    s3=s.slice(r,n-r+1)
    s2.reverse!
    s1.concat(s2)
    s1.concat(s3)
    
    puts s1.join("") 
       

    上がコンテスト中に提出したコード。(2021-12-25 21:39:42)

    Cで書いたのが以下。(2024-11-25 22:29:42)

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 15
    #define N2 1e5+2
    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 l=readint(s1);
        int i=0;
        while(*(s1+i)!=32)i++;
        i++;
        int r=readint(s1+i);
        char* s=(char*)malloc(sizeof(char)*N2);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N2,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        l--;r--;
        while(l<r){
            char temp=*(s+l);
            *(s+l)=*(s+r);
            *(s+r)=temp;
            l++;r--;
        }
        printf("%s\n",s);
        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;
    }
        

    文字の入れ替え

    ABC236-A

    問題概要

    与えられた文字列のa文字目とb文字目を入れ替えたものを出力せよ。

    解答

    s=gets.chomp
    a,b=gets.chomp.split(" ").map(&:to_i)
    t=s[a-1]
    s[a-1]=s[b-1]
    s[b-1]=t
    puts s
        

    この回はコンテスト不参加。開催が日曜日だったからこの日にコンテストがあることを認識していなかったのかな。それで、今回、上のRubyのコード(2024-11-27 22:50:17)と下のCのコード(2024-11-29 22:29:44)を両方書いた。

    #include<stdio.h>
    #include<stdlib.h>
    #define N1 12
    #define N2 6
    int readint(char *s);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N1);
        if (s==NULL){
            printf("memory allocation to s failed.\n");
            exit(1);
        }
        char *ts=fgets(s,N1,stdin);
        if(ts==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        char* s2=(char*)malloc(sizeof(char)*N2);
        if (s2==NULL){
            printf("memory allocation to s2 failed.\n");
            exit(1);
        }
        char *ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        int a=readint(s2);
        a--;
        int i=0;
        while(*(s2+i)!=32)i++;
        i++;
        int b=readint(s2+i);
        b--;
        char t=*(s+a);
            *(s+a)=*(s+b);
        *(s+b)=t;
        printf("%s\n",s);
        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;
    }
        

    文字のソート

    ABC242-B

    問題概要

    与えられた文字列を、文字を辞書順に並び変えて出力せよ。

    解答

    s=gets.chomp.split("")
    puts s.sort.join("")
        

    上がまずコンテスト中に提出したコード。(2022-03-05 21:15:57)

    Cで書いたのが以下。(2023-01-24 17:04:39)

    #include<stdio.h>
    #include<stdlib.h>
    #define N (2e+5)+1 
    void print_ivector(int* a,int n);
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N);
        fgets(s,N,stdin);
        int n=0;
        for(int i=0;;i++){
            if((int)*(s+i)<97 || (int)*(s+i)>122)break;
            else n++; 
        }
        int a[26];
        for(int i=0;i<26;i++)*(a+i)=0;
        for(int i=0;i<n;i++){
    //        printf("%c %d\n",*(s+i),(int)*(s+i));
            *(a+((int)*(s+i)-97))+=1;
    //        a[(int)*(s+i)-97]++;
        }
        /*
        print_ivector(a,26);
        */
        for(int i=0;i<26;i++){
            for(int j=0;j<*(a+i);j++)printf("%c",i+97);
        }
        printf("\n");
        return 0;
    }
    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));
        /*
        for(int i=0;i<n;i++){
            printf("%d\n",*(a+i));
        }
        */
    }
        

    rubyのsortメソッドを使えば文字列でもソートできるので、すぐに終わる。一方、言語が提供するソートメソッドを使わずにやったのが上のCで書いたコード。各文字をアスキーコードに置き換えてソートするわけだが、数字の範囲がアスキーコードが97を引くと0から25に限られるので、バケツソートでソートした。


    文字列の最後の文字

    ABC244-A

    問題概要

    長さnの文字列sの最後の文字を出力せよ。

    解答

    n=gets.to_i
    s=gets.chomp
    puts s[n-1]
        

    このときは本番不参加。Rubyで書いたのが上のコード。(2023-01-14 12:49:39 )

    Cで書いたのが以下。(2024-03-08 22:49:19)

    #include<stdio.h>
    #include<stdlib.h>
    #define N 6
    #define N2 1002
    int main(void){
        char* s=(char*)malloc(sizeof(char)*N);
        if(s==NULL){
            printf("memory allocation to s failed\n");
            exit(1);
        }
        char* ts1=fgets(s,N,stdin);
        if(ts1==NULL){
            printf("fgets(s) failed\n");
            exit(1);
        }
        int n=readint(s);
        free(s);
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to s2 failed\n");
            exit(1);
        }
        char* ts2=fgets(s2,N2,stdin);
        if(ts2==NULL){
            printf("fgets(s2) failed\n");
            exit(1);
        }
        printf("%c\n",*(s2+n-1));
        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;
    }
        

    ABC248-A

    問題概要

    与えられた文字列には、0~9の数字のうち1つだけが現れず、他の9つは1度ずつ出現する。現れない数字を出力せよ。

    解答

    s=gets.chomp.split("").to_a.map(&:to_i)
    a=(0..9).to_a
    b=a-s
    puts b[0]
        

    上がコンテスト中に提出したコード。(2022-04-16 21:02:40)

    Cで書いたのが以下。(2025-02-12 22:11:21)

    #define N1 11
    #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);
        }
        fgets(s1,N1,stdin);
        for(char i=48;i<=57;i++){
            int flag=0;
            for(int j=0;j<9;j++){
                if(*(s1+j)==i){
                    flag=1;
                    break;
                }
            }
            if(flag==0){
                printf("%c\n",i);
                exit(0);
            }
        }
        return 0;
    }
        

    Rubyで書いたときは、出てくる数字の配列を作り、それを 0..9 を要素とする配列から引いた作って出来た数列の唯一の要素を答えとしている。一方、Cで書いたときは、 0..9 のそれぞれについて与えられた文字列に出てくるかどうかを調べ、出てこないものが見つかったらそれを答えとしている。