2022年10月30日日曜日

配列の判定・操作(1)

環状配列上で隣接する数か

ABC240-A

問題概要

1から10までの数がこの順で並ぶ環状配列において、aとbが隣接しているか判定せよ。(a<b)

解答

a,b=gets.chomp.split(" ").map(&:to_i)
if a==1 and b==10 then
    puts "Yes"
elsif
    (b+10-a)%10==1
    puts "Yes"
else
    puts "No"
end
    

上が本番で提出したコード。しかし、なぜ単純にb-aとするのではなく、(b+10-a)%10==1としたのか、今となってはわからない。下がCで書いたコードだが、こちらは単にb-aとして問題なく動いている。

#include<stdio.h>
int main(void){
    int a,b;
    scanf("%d %d\n",&a,&b);
    if ((a==1 && b==10) || b-a==1){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
    

連鎖移動

ABC241-A

問題概要

0..9の数字を要素とする配列aがある。はじめのインデックスは0。「現在のインデックスに対応する値を新しいインデックスとする。」ことを3回繰り返す。最終時点での値を求めよ。

解答

a=gets.chomp.split(" ").map(&:to_i)
idx=0
3.times do
    idx=a[idx]
end
puts idx
    

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

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 10
#define T 3
int main(void){
    int* a=(int*)malloc(sizeof(int)*N);
    for(int i=0;i<N;i++){
        scanf("%d",a+i);
    }
    int idx=0;
    for(int i=0;i<T;i++){
        idx=*(a+idx);
    }
    printf("%d\n",idx);
    return 0;
}
    

ABC241-B

問題概要

自然数を要素とする長さnの配列aがある。i=1..mにわたり、i回目に値b[i]を配列aから取り除く。m回目の操作までの間に、取り除くべき数値が配列aからすでに無くなっているか判定せよ。(なくなっていればNo, なくなっていなければYesを出力)

解答

def bsearch(a,x)
    n=a.size
    l=0
    r=n
    while r-l>0.9 do
        m=(r+l)/2
#        puts "l=#{l} r=#{r}"
        if a[m]<x then
            l=m+1
        else
            if a[m]==x && (m==0 || a[m-1]<x) then
                return m
            else
                r=m
            end
        end
    end
    return -1
end
n,m=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
b=gets.chomp.split(" ").map(&:to_i)
a.sort!
m.times do |i|
    t=bsearch(a,b[i])
    if t==-1
        puts "No"
        exit
    else
        a.delete_at(t)
    end
end
puts "Yes"
    

m回にわたり、配列aにb[i]が含まれているかどうかを二分探索で調べ、含まれていればその値をaから取り除くことを繰り返している。

上が本番での提出コード。Cで書いたのが以下だが、まずTLE等になった次のコードを掲げる。

typedef struct node{
    struct node *prev;
    struct node *next;
    int v;
} node;
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#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);
    int n=readint(s1);
    int i=0;
    while(*(s1+i)>=48 && *(s1+i)<=57)i++;
    i++;
    int m=readint(s1+i);
    char* s2=(char*)malloc(sizeof(char)*(11*n+1));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    fgets(s2,(11*n+1),stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,(11*n+1));
    free(s2);
    char* s3=(char*)malloc(sizeof(char)*(11*m+1));
    if(s3==NULL){
        printf("memory allocation to s3 failed\n");
        exit(1);
    }
    fgets(s3,(11*m+1),stdin);
    int* b=(int*)malloc(sizeof(int)*m);
    if(b==NULL){
        printf("memory allocation to b failed\n");
        exit(1);
    }
    input_iarray(s3,b,m,(11*m+1));
    free(s3);
    node* nds=(node*)malloc(sizeof(node)*n);
    if(nds==NULL){
        printf("memory allocation to nds failed\n");
        exit(1);
    }
    node *first=nds;
    node *end=(nds+n-1);
    for(int j=0;j<n;j++){
        if(j==0 && j==n-1){
            (nds+j)->v=*a;
        }else if(j==0){
            (nds+j)->prev=NULL;
            (nds+j)->next=(nds+1);
            (nds+j)->v=*a;
        } else if(j==n-1){
            (nds+j)->prev=(nds+n-2);
            (nds+j)->next=NULL;
            (nds+j)->v=*(a+n-1);
        } else {
            (nds+j)->prev=(nds+j-1);
            (nds+j)->next=(nds+j+1);
            (nds+j)->v=*(a+n-1);
        }
    }
/*
    printf("n=%d m=%d\n",n,m);
    print_ivector(a,n);
    print_ivector(b,m);
    struct node* k=first;
    do {
        printf("%d ",k->v);
    }while((k->next)!=NULL);
    printf("\n");
*/
    for(int j=0;j<m;j++){
        int found=0;
        while(1) {
            node *nd=first;
            if ((nd->v)==*(b+j)){
                found=1;
                if(nd->prev==NULL && nd->next==NULL){
                    nd=NULL;
                }
                else if(nd->prev==NULL){
                    first=(nd->next);
                    (nd->next)->prev=NULL;
                }else if(nd->next==NULL){
                    end=(nd->prev);
                    (nd->prev)->next=NULL;
                }else{
                    (nd->prev)->next=(nd->next);
                    (nd->next)->prev=(nd->prev);
                }
                break;
            }
            if(nd==end){
                break;
            }else{
                nd=(nd->next);
            }
        }
        if(found==0){
            printf("No\n");
            exit(0);
        }
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

上のコードは一部のテストケースではACになるものの、他はTLEになったりWAになったりする。TLEと言っても、この問題の制約ではaもbも要素数は最大で1000個だから、計算量の問題ではない。どうも、whileループが無限ループになっているようだ。そこで、Stackoverflowで質問した。

actorbugさんから回答を頂いたが、やはり、whileループが無限ループになっていて、node *nd=first;という一文をwhileループの中においてしまっていたため、毎回先頭に戻ることになってしまっていたのだった。

これに加えて、他にも見つけた誤りを修正したのが次のコード。これは、一つだけREになった。

typedef struct node{
    struct node *prev;
    struct node *next;
    int v;
} node;
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#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);
    int n=readint(s1);
    int i=0;
    while(*(s1+i)>=48 && *(s1+i)<=57)i++;
    i++;
    int m=readint(s1+i);
    char* s2=(char*)malloc(sizeof(char)*(11*n+1));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    fgets(s2,(11*n+1),stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,(11*n+1));
    free(s2);
    char* s3=(char*)malloc(sizeof(char)*(11*m+1));
    if(s3==NULL){
        printf("memory allocation to s3 failed\n");
        exit(1);
    }
    fgets(s3,(11*m+1),stdin);
    int* b=(int*)malloc(sizeof(int)*m);
    if(b==NULL){
        printf("memory allocation to b failed\n");
        exit(1);
    }
    input_iarray(s3,b,m,(11*m+1));
    free(s3);
    node* nds=(node*)malloc(sizeof(node)*n);
    if(nds==NULL){
        printf("memory allocation to nds failed\n");
        exit(1);
    }
    node *first=nds;
    node *end=(nds+n-1);
    for(int j=0;j<n;j++){
        if(j==0 && j==n-1){
            (nds+j)->v=*a;
        }else if(j==0){
            (nds+j)->prev=NULL;
            (nds+j)->next=(nds+1);
            (nds+j)->v=*a;
        } else if(j==n-1){
            (nds+j)->prev=(nds+n-2);
            (nds+j)->next=NULL;
            (nds+j)->v=*(a+n-1);
        } else {
            (nds+j)->prev=(nds+j-1);
            (nds+j)->next=(nds+j+1);
            (nds+j)->v=*(a+j);
        }
    }
/*
    printf("n=%d m=%d\n",n,m);
    print_ivector(a,n);
    print_ivector(b,m);
    struct node* k=first;
    do {
        printf("%d ",k->v);
    }while((k->next)!=NULL);
    printf("\n");
*/
    for(int j=0;j<m;j++){
        int found=0;
        node *nd=first;
        while(1) {
            if ((nd->v)==*(b+j)){
                found=1;
                if(nd->prev==NULL && nd->next==NULL){
                    nd=NULL;
                }
                else if(nd->prev==NULL){
                    first=(nd->next);
                    (nd->next)->prev=NULL;
                }else if(nd->next==NULL){
                    end=(nd->prev);
                    (nd->prev)->next=NULL;
                }else{
                    (nd->prev)->next=(nd->next);
                    (nd->next)->prev=(nd->prev);
                }
                break;
            }
            if(nd==end){
                break;
            }else{
                nd=(nd->next);
            }
        }
        if(found==0){
            printf("No\n");
            exit(0);
        }
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

その、REになるテストケースを見ると、

1 1
1
1
という内容だ。

Paiza.ioにこのデータを入れてみると、Yesという正解になるのだが、AtCoderのコードテストで試してみると、終了コード139と、やはりエラーになるようだ。n=1,m=1というのは入力例2と同じ。違うのは、入力例2ではaとbのそれぞれの唯一の要素は異なる値だが、こちらではそれが同じ値だということ。

nd->prev==NULL && nd->next==NULL の場合に

        if(j==m-1){
            printf("Yes\n");
            exit(0);
        }else{
            printf("No\n");
            exit(0);
        }
    
という微修正を加えてみたが、結果は同じ。一度諦めてから翌日もう一度コードを見直してみて、最初に配列から連結リストにデータを入れなおした際、データ数が1個の場合にその前後をNULLにする処理をしていなかったことに気づく。しかし、これでよく nd->next==NULL を停止条件にしている入力確認のためのコードでエラーにならなかったなと思う。この確認はPaizaでしていて、コード全体もPaizaでは成功しているから、Paizaでは自動的に何も入れてないところはNULLになっているということか。

最終的にACになったコードは以下。

typedef struct node{
    struct node *prev;
    struct node *next;
    int v;
} node;
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#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);
    int n=readint(s1);
    int i=0;
    while(*(s1+i)>=48 && *(s1+i)<=57)i++;
    i++;
    int m=readint(s1+i);
    char* s2=(char*)malloc(sizeof(char)*(11*n+1));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    fgets(s2,(11*n+1),stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,(11*n+1));
    free(s2);
    char* s3=(char*)malloc(sizeof(char)*(11*m+1));
    if(s3==NULL){
        printf("memory allocation to s3 failed\n");
        exit(1);
    }
    fgets(s3,(11*m+1),stdin);
    int* b=(int*)malloc(sizeof(int)*m);
    if(b==NULL){
        printf("memory allocation to b failed\n");
        exit(1);
    }
    input_iarray(s3,b,m,(11*m+1));
    free(s3);
    node* nds=(node*)malloc(sizeof(node)*n);
    if(nds==NULL){
        printf("memory allocation to nds failed\n");
        exit(1);
    }
    node *first=nds;
    node *end=(nds+n-1);
    for(int j=0;j<n;j++){
        if(j==0 && j==n-1){
            nds->prev=NULL;
            nds->next=NULL;
            (nds+j)->v=*a;
        }else if(j==0){
            (nds+j)->prev=NULL;
            (nds+j)->next=(nds+1);
            (nds+j)->v=*a;
        } else if(j==n-1){
            (nds+j)->prev=(nds+n-2);
            (nds+j)->next=NULL;
            (nds+j)->v=*(a+n-1);
        } else {
            (nds+j)->prev=(nds+j-1);
            (nds+j)->next=(nds+j+1);
            (nds+j)->v=*(a+j);
        }
    }
/*
    printf("n=%d m=%d\n",n,m);
    print_ivector(a,n);
    print_ivector(b,m);
    struct node* k=first;
    do {
        printf("%d ",k->v);
    }while((k->next)!=NULL);
    printf("\n");
*/
    for(int j=0;j<m;j++){
        int found=0;
        node *nd=first;
        while(1) {
            if ((nd->v)==*(b+j)){
                found=1;
                if(nd->prev==NULL && nd->next==NULL){
                    if(j==m-1){
                        printf("Yes\n");
                        exit(0);
                    }else{
                        printf("No\n");
                        exit(0);
                    }
                }
                else if(nd->prev==NULL){
                    first=(nd->next);
                    (nd->next)->prev=NULL;
                }else if(nd->next==NULL){
                    end=(nd->prev);
                    (nd->prev)->next=NULL;
                }else{
                    (nd->prev)->next=(nd->next);
                    (nd->next)->prev=(nd->prev);
                }
                break;
            }
            if(nd==end){
                break;
            }else{
                nd=(nd->next);
            }
        }
        if(found==0){
            printf("No\n");
            exit(0);
        }
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

配列に含まれない最小の非負整数

ABC245-B

問題概要

非負整数を要素とする長さnの配列aが与えられる。aに含まれない最小の非負整数を求めよ。

解答

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
a.sort!
b=(0..n).to_a
c=b-a
puts c.min
    

上が本番で提出したコード。見直してみると、aをソートしている意味がないし、bは0からaの全範囲の一つ上までを含む配列を作ればよいので、制約によりa[i]の最大値が2000であることを考え、b=(0..2000+1).to_aとするか、或いはb=(0..a.max+1).to_a としてもよかった。

やり直したのが以下のコード。配列の引き算を使っていない分、こちらのほうがいいかと思う。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
ref=Array.new(2002,false)
n.times do |i|
    ref[a[i]]=true
end
(0..2001).each do |i|
    if ref[i]==false
        puts i
        exit
    end
end
    

Cで書いたのが以下。

void qsort_1(int* a, int l, int r);
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#define N 10002
#define N1 6
#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);
    int n=readint(s1);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s3,a,n,N);
//    print_ivector(a,n);
    qsort_1(a,0,n-1);
//    print_ivector(a,n);
    int ans=0;
    int i=0;
    do {
        if(a[i]==ans){
            ans++;
            if(i==(n-1))break;
            else{
                i++;
                while(i<n-1 && a[i]<ans)i++;
                }
        }
        else break;
    }while(i<n);
    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;
}
void qsort_1(int* a, int l, int r){
    int left, right, d, temp;
    if (l>=r)return;
    d=*(a+l);
    for(left=l,right=r;left<right;){
        while(left<=right && a[left]<=d)left++;
        while(left<=right && a[right]>d)right--;
        if(left<right){
            temp=a[left];
            a[left]=a[right];
            a[right]=temp;
        }
    }
    temp=a[l];
    a[l]=a[right];
    a[right]=temp;
    qsort_1(a,l,right-1);
    qsort_1(a,right+1,r);
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

まず配列をソートし(今回は実際に必要なソート。クイックソートを(ほぼ本の引き写しだけだけど)初めて実装した)、答えの候補を最小の0から始め、1ずつインクリメントしながら、それが配列の中に含まれなくなる所を探している。配列の最小値と答えの候補が一致した時には答えの候補の値をインクリメントするとともに配列のインデックスもインクリメントするが、配列には同じ値が複数並んでいる可能性があるので、もしインクリメント後のインデックスに対応する値が答えの候補よりも小さければ、配列の値が答えの候補の値以上になるまでインデックスをインクリメントしている。


分類無し

ABC247-B

問題概要

それぞれ2個の文字列からなるn組の文字列の組が与えられる。各組の第一要素と第二要素のいずれか一方のみを選ぶとき選ばれた全てが、他の各組の第1または第2要素と一致しないようにすることができるか。

解答

n=gets.to_i
a=[]
n.times do |i|
    a[i]=gets.chomp.split(" ")
end
flag=true
n.times do |i|
    b=a.dup
    b.delete_at(i)
    b.flatten!
    if b.include?(a[i][0]) && b.include?(a[i][1])
        flag=false
    end
end
if flag==true
    puts "Yes"
else
    puts "No"
end
    

上が本番で提出したコードなのだが、自分で半年余りの後に読み返してみて、何をやっているのかしばらく分からなかった。

これは、各組について、その第一要素と第二要素のいずれかが、他の組の中で出現する文字列と一致していなければ、その組は他の組と被らない文字列を持つことができることを使っている。そこで、各組ごとに、全文字列からその組の文字列二つを除いた要素数2n-2の配列を作り、その組の第一文字列か第二文字列のどちらかが、その要素数2n-2の配列に含まれていなければその組は大丈夫として(具体的には、第一文字列も第二文字列も要素数2n-2の配列に含まれていればフラグをfalseにして)、全ての組が大丈夫であれば(flagがfalseになっていなければ)、「作ることができる」と判定しているのである。(自分が書いたコードなのに、人が書いたコードの解説をしている気分だ。)

Cで書いたのが以下。

typedef struct nom {
    char first[11];
    char last[11];
}nom;
int scmp1(char* s, char* t, int n);
int readint(char *s);
#define N1 5
#define N2 23
#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);
    int n=readint(s1);
    nom* nms=(nom*)malloc(sizeof(nom)*n); 
    if(nms==NULL){
        printf("memory allocation to nms failed\n");
        exit(1);
    }
    for(int j=0;j<n;j++){
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to s2(%d) failed\n",j);
            exit(1);
        }    
        fgets(s2,N2,stdin);
        int k=0,l=0;
        while(*(s2+k)!=32){
            (nms+j)->first[k]=*(s2+k);
            k++;
        }
        (nms+j)->first[k]=0;
        k++;
        while(*(s2+k)>=97 && *(s2+k)<=122){
            (nms+j)->last[l]=*(s2+k);
            k++;l++;
        }
        (nms+j)->last[l]=0;
    }
/*
    for(int j=0;j<n;j++){
        printf("---nms(%d)---\n",j);
        printf("%s\n",(nms+j)->first);
        printf("%s\n",(nms+j)->last);
    }
*/
    int flag_first=1,flag_last=1;
    for(int j=0;j<n;j++){
        for(int h=0;h<n;h++){
            if(h==j)continue;
            if(scmp1((nms+j)->first,(nms+h)->first,10)==0){
                flag_first=0;
            }
            if(scmp1((nms+j)->first,(nms+h)->last,10)==0){
                flag_first=0;
            }
            if((scmp1((nms+j)->last,(nms+h)->first,10)==0)){
                flag_last=0;
            }
            if((scmp1((nms+j)->last,(nms+h)->last,10)==0)){
                flag_last=0;
            }
        }
        if(flag_first==0 && flag_last==0){
            printf("No\n");
            exit(0);
        }
    }
    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;
}
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");
        }
    }
}
    

ただ、上のコードはACにはなったものの、想定外の動作が残る部分があった。scmp1関数の中の if(i>=n) 条件文の中身をコメントアウトなしで実行すると、例えば次の例(テストケース名は02_except_one_00)

    100
    avmlajlxrf zttmjemxex
    vauvmzqhzs uzgvclqnem
    lcljwpbfsg xnbsoouwsn
    zifcqxivtq gjcupzgkyz
    cinpwuotzy yxojlpipjn
    tdnemryudq skxibydrct
    tdjdtgtipl eurwjwxvyv
    abzttwzxhb amycbijxta
    vwzjmmlaew yxojlpipjn
    xteucdfimo syiwqxsyvy
    vowbgfxsqv sjcegwybmg
    urdgjzthal cckapzefmh
    jlqemwztfg yzlusdaezt
    ejwwcutzsa atxkasugou
    fvvvsecqvg csloaexqar
    xdudibkgmt dmzuyjjkfu
    jnmfxnucto anlkpmegdk
    hlwnwahibo wdwgecsbqe
    xoeqypczez txgzzkdeco
    vrwhijewvl wytsxzogzb
    flocshjexi vrounuangs
    giiikhuvlw eapudjjlxz
    asvwjrhpck oyyrexwiri
    cqsqohapin ddpdojipiq
    hgtmfsuedp tkjqmgetkl
    emrjmdsahn urmlzavbyc
    odultqflzd obnwbmbrbv
    jhfxadevqt cinpwuotzy
    dyvarcvxms tduoqcljzw
    pcumzuyzgs xbfsoxvtlx
    slzgvyptkq begswrioai
    sikliwzvqd ivkzfnjnqs
    fxfomljyeo rohrqgsbrr
    mwtptktucb gabwlmmezw
    swydbovjqx ybfbjxmlkw
    qhpbuccyhu dsaxxwfler
    guqxzzgrlr ftimlzorzv
    bqklofbwzb pyemgoqbqp
    xnzsfrgfqd fctpafbged
    safprjkxav iducfibsms
    bgaczxkjjo qdubpubmqy
    gjfgwuapdu mxngjzuyzs
    gfmrbrczyv wkybysnbzh
    cpqnmbijhc tisbxwevsl
    yyepttinlp qxdlcyaryl
    rtfvtlsaed gxnmgfdlaz
    cztjbttosy xvgevjycvc
    niqyddonks pcntudhjex
    eprhqytehu gxoechnwzq
    uewhzvtawt hdkzmpoxdd
    qrtpqcgzid jmdnedpvde
    cbobbqkhvo fecwfsshrs
    lxlwxjdune nbfsjuwwfl
    nfolbzlgjz ayiiidkxpv
    secemmlcyo binjogcrin
    evzuvmvvxi wsxrlspmwe
    edufzrlfzz uhzqiupfoc
    cjehxnsfew lfxcujwugl
    vfvxpvhkzp zwhyamzuqc
    bmhndupair jmijqprnqv
    rrxvdkpxxe comgvwojrd
    shhltqafiy axpqnlimij
    qwjlqpuqyr rwrkgculnr
    isiadtwyew gmgrjchxeq
    asydjlrgnu rsqxsdaeke
    byeogugzvd oimjblsaqp
    etfzxishqr zicggawrny
    vrkvpixskp pootisasgb
    jyquzkympl kpgrarjurw
    irokbsgmzn axnmijtbuo
    jcicrhohws glmxshpofv
    mlcltqliqd edgalwqyrz
    qabdaqkuvk gpwzxamavm
    rjuybctves qoltuirrbt
    ceehfrnnlt wzqpfbegeb
    bygsepvzvs wivbkxcwwr
    bztgqkaszz kbddlinrdl
    bgcwkfhjfd anvaxdphvm
    hmkrfrrykj tposlkgyfu
    cvpzdchrcf jrkecgpdod
    ziyqvltwjr jjywyobhow
    mspuosmxsw plkwluwjpg
    lnxxowqjmu eggxviqrxd
    dgwlamwtdf svrlcuukan
    edrefbtrce khqwssekyx
    uhcvrasuxf vtvrbfeswq
    jupyipafzd ufewjurxbj
    bltjuxbtkv zmfhstxyzx
    zgcxjwddcs xafyqtugis
    glbrpmckrt xqxemkieyn
    epbfldpccl idkkwdfvrc
    rlcsstqdlx rvdzydkjnp
    rhknmgmqdq xyumddhczm
    ignokmfecc tizaaxfkgh
    zlinrzhweq zzdpcqbjvc
    emehoqjlcq gmvctsblgw
    yargqiezlu oyifpuekox
    ntveljeitx coyexswfvj
    nbejnmvkdn amgirkcihn
    lxlaawoatn qjofmdhvwt
で、
    s=yxojlpipjn, t=yxojlpipjn, i=10
    Comparing failed.
    s=cinpwuotzy, t=cinpwuotzy, i=10
    Comparing failed.
という出力がされてしまうのである。

文字列の文字数が10文字、末尾のヌル文字も含めて11文字あるとき、i=0からはじめたカウンターが11回廻ってi==n(=10)になったときには if (*(s+i)==0 && *(t+i)==0) return 0; の文が実行され、その後に来ている if(i>=n) という条件分岐の所に行くことはないはずなのに、なぜだ、と悩んだ。

scmp1関数の実装がおかしいのかと考えて、

    char str1[4]="abc";
    char str2[4]="abc";
    scmp1(str1,str2,4);
というコードを試しに入れてみたが、こちらでは上の現象は起きない。

原因が分からず、結局 Stackoverflow で質問した。

回答を頂いて分かったことは、例えば上のテストケース02_except_one_00のような文字数10文字で同じ文字列があるときには、i=9(10回目のループ)で最後の文字の比較が行われ、それが同じ文字なので else i++; が実行されてi=10となり、その直後のif(i>=n)以下が実行されてしまうということである。つまり、この場合、scmp1関数の第三引数として渡すべきは10ではなく11だったということになる。文字数が最大10文字だから、11文字目のヌル文字の比較が行われるのはi=0から始めてi=10のとき、だから i==10 となるかで上限チェックをすればいいと思い込んでいたのが間違いだったのである。

それに、よく見れば、abcとabcを比較した時にはscmp1関数の第三引数に文字数+1の4を渡している。だから、それと同じならば、文字数が10の時にはscmp1関数の第三引数は11でなければならないはずだったのだ。分かってみれば当たり前のことなのだが、自分では気づかないものだと実感する。

以下が、そこを修正したコード。先に掲げたコードは、ACにはなったが厳密にはバグのあるコードということになる。

typedef struct nom {
    char first[11];
    char last[11];
}nom;
int scmp1(char* s, char* t, int n);
int readint(char *s);
#define N1 5
#define N2 23
#define N3 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);
    int n=readint(s1);
    nom* nms=(nom*)malloc(sizeof(nom)*n); 
    if(nms==NULL){
        printf("memory allocation to nms failed\n");
        exit(1);
    }
    for(int j=0;j<n;j++){
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to s2(%d) failed\n",j);
            exit(1);
        }    
        fgets(s2,N2,stdin);
        int k=0,l=0;
        while(*(s2+k)!=32){
            (nms+j)->first[k]=*(s2+k);
            k++;
        }
        (nms+j)->first[k]=0;
        k++;
        while(*(s2+k)>=97 && *(s2+k)<=122){
            (nms+j)->last[l]=*(s2+k);
            k++;l++;
        }
        (nms+j)->last[l]=0;
    }
/*
    for(int j=0;j<n;j++){
        printf("---nms(%d)---\n",j);
        printf("%s\n",(nms+j)->first);
        printf("%s\n",(nms+j)->last);
    }
*/
    int flag_first=1,flag_last=1;
    for(int j=0;j<n;j++){
        for(int h=0;h<n;h++){
            if(h==j)continue;
            if(scmp1((nms+j)->first,(nms+h)->first,N3)==0){
                flag_first=0;
            }
            if(scmp1((nms+j)->first,(nms+h)->last,N3)==0){
                flag_first=0;
            }
            if((scmp1((nms+j)->last,(nms+h)->first,N3)==0)){
                flag_last=0;
            }
            if((scmp1((nms+j)->last,(nms+h)->last,N3)==0)){
                flag_last=0;
            }
        }
        if(flag_first==0 && flag_last==0){
            printf("No\n");
            exit(0);
        }
    }
    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;
}
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");
        }
    }
}
    

配列の生成

ABC247-C

問題概要

列 S を次のように定義します。

  1. s[1]は 1 つの 1 からなる長さ 1 の列である。
  2. S[n](n は 2 以上の整数) は S[n−1],n,S[n−1]をこの順につなげた列である。
たとえば S[2]はS[1],2,S[1]をこの順につなげた列なので 1,2,1 である。 S[3]はS[2],3,S[2]をこの順につなげた列なので 1,2,1,3,1,2,1 である。 N が与えられるので、列 S[n]をすべて出力してください。

解答

問題の定義の通りに処理する。具体的には、nが1ならば1を出力。nが2以上ならば、初期値としてa=[1]を作り、あとは2からnに至るまで、a[n]=a[n-1](連結)[n](連結)a[n-1]という漸化式で新しい配列を作り、それを出力していく。見直してみると、nが1の時の処理を分ける必要はなかったが、まあいい。

n=gets.to_i
a=[]
if n==1
    puts 1
    exit
else
    a=[1]
    (2..n).each do |i|
        a=a+[i]+a
    end
end
puts a.join(" ")
    

上が本番で提出したコード。C問題としては珍しく簡単だった。

さて、これをCでやるとなると、まず、全部で何項の数列になるかを計算しなければならない。a[n]の項数をc[n]とすると、c[1]=1, c[n+1]=1+2*c[n] だから、c[n+1]+1=2*(c[n]+1), c[1]+1=2 となり、c[n]+1=2*2^(n-1)=2^n で、c[n]=2^n-1 となる。n=1,2,3,4 の場合について、入力例からこれが正しいことが確認できる。

それで書いたのが以下。

void print_ivector(int* a,int n);
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);
    }
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int num,oldnum;
    int *new;
    for(int i=1;i<=n;i++){
        num=((1<<i)-1);
        if(i==1){
            int* temp=(int*)malloc(sizeof(int)*num);
            if(temp==NULL){
                printf("memory allocation to temp(i=1) failed\n");
                exit(1);
            }
            new=temp;
            *new=1;
            oldnum=1;
        }else{
//            printf("num=%d\n",num);
//            printf("oldnum=%d\n",oldnum);
            int* temp=(int*)realloc(new,sizeof(int)*num);
            if(temp==NULL){
                printf("memory allocation to temp(i=%d) failed\n",i);
                free(new);
                exit(1);
            }
            new=temp;
            for(int j=0;j<oldnum;j++){
//                printf("j=%d\n",j);
                *(new+num/2+1+j)=*(new+j);
            }
            *(new+num/2)=i;
            oldnum=num;
        }
    }
    print_ivector(new,num);
    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;
}
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));
}
    

配列の判定・操作(2)


先頭から削除、末尾に挿入

ABC278-A

問題概要

要素数nの配列に対し、先頭の要素一つを削除し、末尾に0を一つ追加することを繰り返す。k回の操作後にできた配列を出力せよ。

解答

この問題の制約ならばk回のshift, pushを繰り返しても間に合うのだが、それではあまりにも芸がない気がするので、「先頭のk要素を削除し、末尾に0をk個追加する」ことを考える。

まず書いたのが、次のコード

n,k=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
a.shift(k)
b=[0]*k
a+=b
puts a.join(" ")
    

提出してみたら、半分くらいがWA

少し考えてみると、このコードでは、kがnよりも大きい場合に正しい結果が得られないことに気づく。

それで、書き直したのが以下のコード

n,k=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
unless k>n
    a.shift(k)
    b=[0]*k
    a+=b
else
    a=Array.new(n,0)
end
puts a.join(" ")
    

これを提出してACになった。

もう一つのやり方としては、先にaの末尾に0がk個ある配列を連結してしまい、あとは配列a自体は変更することなく、そのk+1番目の要素からn要素を出力するというやりかたもある。

そうやってみたのが、以下のコード。

n,k=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
b=[0]*k
a+=b
a=a[k,n]
puts a.join(" ")
    

これもACになった。

Cで書いたのが以下。

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 9
#define N2 402
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    if(s==NULL){
        printf("memory allocation to s failed\n");
        exit(1);
    }
    fgets(s,N,stdin);
    int n=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int k=readint(s+i);
    free(s);
    char* s2=(char*)malloc(sizeof(char)*N2);
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    fgets(s2,N2,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,N2);
//    print_ivector(a,n);
    int * b=(int*)malloc(sizeof(int)*n);
    if(b==NULL){
        printf("memory allocation to b failed\n");
        exit(1);
    }
    if(k>n)k=n;
    for(int j=0;j<(n-k);j++){
        *(b+j)=*(a+k+j);
    }
    for(int j=0;j<k;j++){
        *(b+(n-k)+j)=0;
    }
    print_ivector(b,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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

先頭に元の配列のk番目から(n-k)個の要素をコピーし、残りを0で埋めた配列を出力している。


逆順に出力

ABC284-A

問題概要

n個の文字列が順に与えられるので、これを後から与えられたものから順に出力せよ

解答

n=gets.to_i
as=[]
n.times do |i|
    s=gets.chomp
    as.push(s)
end
(n-1).downto(0).each do |i| 
    puts as[i]
end    

Cでまず書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 4
#define N2 111
#define N3 12
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    if(s==NULL){
        printf("memory allocation to s failed\n");
        exit(1);
    }
    fgets(s,N,stdin);
    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);
    }
    fgets(s2,N2,stdin);
    for(int i=0;i<n;i++){
        char* s3=(char*)malloc(sizeof(char)*N3);
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        fgets(s3,N3,stdin);
        int j=0;
        while(*(s3+j)!=0){
            *(s2+11*i+j)=*(s3+j);j++;
        }
        *(s2+11*i+j)=0;
    }
    for(int i=n-1;i>=0;i--){
        printf("%s",(s2+11*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;
}
    

このコードは、実行すると、例えば

4
2023
Year
New
Happy
    
という入力例では、出力結果が、
    Happy
    New
    Year
となり、最後の(元の並びでは最初の)文字列が出力されない。

原因がわからず、StackOverflowで質問。

actorbugさんから回答を頂いて分かったのは、fgets(s2,N2,stdin); という一行が余計だということだ。言われてみれば拍子抜けするような当たり前の話で、このs2というメモリ領域は後で一行ずつ取り込む文字列を順に格納するためのものだから、ここでfgetsしてはいけないのだった。前に書いたコードからメモリ確保部分と一緒にコピー&ペーストで引っ張ってきたという問題もあるのだが、つい、文字列用のメモリを確保したら次はfgetsと思い込んでいてその存在を怪しまなかった。

あと、指摘されたのがfgetsのヌルチェックの必要性。前にmallocの戻り値チェックの必要性を指摘されてから、mallocの後にはヌルチェックを入れるようにしていたが、mallocだけでなく、fgetsでも戻り値チェックが必要なのか。

さて、その問題は解決したが、今度は、一部のテストケースで、改行が出力されずWAになるという現象が生じる。というより、ある意味このコードではこうなることが最初の想定で、だから初めは printf("%s\n",(s2+N3*i)); と改行を入れていたのである。だが、そうすると余分な空行が出力されてしまうので、改行文字を加えずに出力するコードに直していたのである。だから、上のコードのように出力の際に改行文字を加えれはこのテストケースについては正しい出力がえられるが、そうすると今度は改行文字なしで改行されていたテストケースで空行が入ってしまうことは明白である。

この現象の意味についてしばらく考えたが、テストケースの入力例のうち、文字列に最初から改行文字が入っているものとそうでないものがあるのではないかと推測し、もし最後の文字(ヌル文字0の前)が改行文字でない場合には文字列に改行文字を加えるようコードを書きなおした。それに伴い、一つの文字列分のメモリも、(文字数の上限10)+(改行文字分2)+(ヌル文字分1)の13個に改める。それでACになったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 4
#define N3 13
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    if(s==NULL){
        printf("memory allocation to s failed\n");
        exit(1);
    }
    char *ts=fgets(s,N,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    int n=readint(s);
    free(s);
    char* s2=(char*)malloc(sizeof(char)*(N3*n));
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char* s3=(char*)malloc(sizeof(char)*N3);
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        char* ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(ts3(i)) failed\n");
            exit(1);
        }
        int j=0;
        while(*(s3+j)!=0){
            *(s2+N3*i+j)=*(s3+j);j++;
        }
        if(*(s3+N3*i+j-1)!='\n'){
            *(s2+N3*i+j)='\n'; j++;    
        }
        *(s2+N3*i+j)=0;
        free(s3);
    }
    for(int i=n-1;i>=0;i--){
        printf("%s",(s2+N3*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;
}
    

配列の一部の入れ替え

ABC286-A

問題概要

ある配列のp番目からq番目まで(第一部分)とr番目からs番目まで(第二部分)を入れ替えよ。

解答

n,p,q,r,s=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
b=[]
idx=0
(p..q).each do |i|
    b[idx]=a[i-1]
    idx+=1
end
idx=r-1
(p..q).each do |i|
    a[i-1]=a[idx]
    idx+=1
end
idx=0
(r..s).each do |i|
    a[i-1]=b[idx]
    idx+=1
end
puts a.join(" ")
    

swap関数を作る時の要領で、片方の部分の一時保管用の配列を作ってやればよい。

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

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 21
#define N2 401
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    char* ts=fgets(s1,N,stdin);
    if(ts==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    int i=0;
    while(*(s1+i)!=32)i++;
    i++;
    int p=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int q=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int r=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int s=readint(s1+i);
    free(s1);
//    printf("n=%d p=%d q=%d r=%d s=%d\n",n,p,q,r,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);
    }
    int * a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,N2);
//    print_ivector(a,n);
    for(int j=0;j<p-1;j++){
        printf("%d ",*(a+j));
    }
    for(int j=r-1;j<s;j++){
        printf("%d ",*(a+j));
    }
    for(int j=q;j<r-1;j++){
        printf("%d ",*(a+j));
    }
    for(int j=p-1;j<q;j++){
        printf("%d",*(a+j));
        if(j!=n-1)putchar(32);
    }
    for(int j=s;j<n;j++){
        printf("%d",*(a+j));
        if(j!=n-1)putchar(32);
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

こちらでは、特に出力用の配列を作ることはせず、元の配列の要素を、① 1番目からp-1番目、② r番目からs番目、③ q+1番目からr-1番目、⑤s+1番目からn番目、の順に出力している。

配列の判定・操作(3)

間を埋める

ABC301-B

問題概要

与えられた整数列の、間隔が1でないところを整数で埋めて全ての間隔を1にせよ。

解答

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
b=[]
b.push(a[0])
(1...n).each do |i|
    t=a[i]-a[i-1]
    if t.abs==1
        b.push(a[i])
    elsif a[i]>a[i-1]
        temp=((a[i-1]+1)..a[i]).to_a
        b+=temp
    elsif a[i]<a[i-1]
        temp=(a[i]..a[i-1]-1).to_a.reverse
        b+=temp
    end
end
puts b.join(" ")
    

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

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
#define N2 401
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)*N2);
    if(s2==NULL){
        printf("memory allocation to s3 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,N2,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
    int *a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,N2);
    free(s2);
//    print_ivector(a,n);
    printf("%d",*a);
    for(int i=1;i<n;i++){
        putchar(32);
        if(*(a+i-1)-*(a+i)==1 || *(a+i)-*(a+i-1)==1){
            printf("%d",*(a+i));
        }
        else if(*(a+i)<*(a+i-1)){
            int m=*(a+i-1)-*(a+i)-1;
            for(int j=0;j<m;j++){
                if(j!=0)putchar(32);
                printf("%d",*(a+i-1)-j-1);
            }
            putchar(32);
            printf("%d",*(a+i));
        }else if(*(a+i)>*(a+i-1)){
            int m=*(a+i)-*(a+i-1)-1;
            for(int j=0;j<m;j++){
                if(j!=0)putchar(32);
                printf("%d",*(a+i-1)+j+1);
            }
            putchar(32);
            printf("%d",*(a+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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

ABC304-A

問題概要

環状配列において名前と数値がセットで与えられる。数値が最小の部分を起点として出力せよ。

解答

n=gets.to_i
year=Array.new(2*n,0)
name=Array.new(2*n,"")
idx=0
min=1e9
n.times do |i|
    t1,t2=gets.chomp.split(" ")
    y=t2.to_i
    name[i]=name[i+n]=t1
    year[i]=year[i+n]=y
    if y<min
        min=y
        idx=i
    end
end
n.times do |i|
    puts name[idx+i]
end
    

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

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
#define N2 11
#define N3 23
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)*N2*n);
    if(s2==NULL){
        printf("memory allocation to s2 failed\n");
        exit(1);
    }
    int *a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char *s3=(char*)malloc(sizeof(char)*N3);
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        char *ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(s3)(%d) failed\n",i);
            exit(1);
        }
        int j=0;
        while(*(s3+j)>=97 && *(s3+j)<=122){
            *(s2+N2*i+j)=*(s3+j); j++;
        }
        *(s2+N2*i+j)=0;
        j++;
        *(a+i)=readint(s3+j);
        free(s3);
    }
/*    for(int i=0;i<n;i++){
        printf("%s ",s2+N2*i);
        printf("%d\n",*(a+i));
    }*/
    int min_idx=0;
    int min=1e9;
    for(int i=0;i<n;i++){
        if(*(a+i)<min){
            min=*(a+i);
            min_idx=i;
        }
    }
    for(int i=0;i<n;i++){
        int idx=(min_idx+i)%n;
        printf("%s\n",(s2+idx*N2));
    }
    free(s2);
    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;
}
    

ABC308-A

問題概要

長さ8の整数列が与えられる。これが条件「広義単調増加であり、全ての要素は100以上675以下であり、25の倍数である」を満たすか判定せよ。

解答

n=8
a=gets.chomp.split(" ").map(&:to_i)
n.times do |i|
    if a[i]<100 || a[i]>675 || a[i]%25!=0
        puts "No"
        exit
    elsif i>=1 && a[i]<a[i-1]
        puts "No"
        exit
    end
end
puts "Yes"
    

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

void input_iarray(char* s,int* a,int n,int imax);
#include<stdio.h>
#include<stdlib.h>
#define N1 41
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 *a=(int*)malloc(sizeof(int)*8);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s1,a,8,N1);
    free(s1);
    int flag=1;
    for(int i=0;i<7;i++){
        if(*(a+i)>*(a+i+1))flag=0;
        if(*(a+i)<100 || *(a+i)>675)flag=0;
        if(*(a+i)%25!=0)flag=0;
        if(flag==0){
            printf("No\n");
            exit(0);
        }
    }
    printf("Yes\n");
    return 0;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx\n");
}
    

ABC313-A

問題概要

配列の最初の要素(a[0])が他の全ての要素よりも大きくなるようにするには、a[0]に何を加えればよいか。。

解答

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
mx=0
(1...n).each do |i|
    if a[i]>mx
        mx=a[i]
    end
end
if a[0]>mx
    puts 0
else
    puts mx-a[0]+1
end 
    

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

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
#define N2 401
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)*N2);
    if(s2==NULL){
        printf("memory allocation to s3 failed\n");
        exit(1);
    }
    char *ts2=fgets(s2,N2,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
    int *a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,N2);
    free(s2);
//    print_ivector(a,n);
    int max=0;
    for(int i=1;i<n;i++){
        if(*(a+i)>max){
            max=*(a+i);
        }
    }
    int ans=0;
    if((max-*a)>=0){
        ans=max-*a+1;
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

ABC314-B

問題概要

n個の整数列が与えられる。xを含み、かつ要素数が最も少ない配列の番号を昇順で出力せよ。

解答

n=gets.to_i
c=[]
a=[]
ans=[]
n.times do |i|
    c[i]=gets.to_i
    a[i]=gets.chomp.split(" ").map(&:to_i)
end
x=gets.to_i
n.times do |i|
    flag=false
    if a[i].include?(x)
        flag=true
        n.times do |j|
            if a[j].include?(x) && c[j]<c[i]
                flag=false
                break
            end
        end
        if flag==true
            ans.push(i+1)
        end
    end
end
puts ans.size
puts ans.join(" ")
    

Cでまず書いたのが以下のコード。(デバッグ出力用に埋め込んだコードの一部もコメントアウトしないままにしてある。)

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
#define N2 37
#define N3 4
#define N4 102
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);
    int *ars=(int*)malloc(sizeof(int)*n*(N2+1));
    if(ars==NULL){
        printf("memory allocation to ars failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char *s3=(char*)malloc(sizeof(char)*N3);
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        char *ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(s3(%d)) failed\n",i);
            exit(1);
        }
        int c=readint(s3);
        free(s3);
        char *s4=(char*)malloc(sizeof(char)*N4);
        if(s3==NULL){
            printf("memory allocation to s4(%d) failed\n",i);
            exit(1);
        }
        char *ts4=fgets(s4,N4,stdin);
        if(ts4==NULL){
            printf("fgets(s4(%d)) failed\n",i);
            exit(1);
        }
        int *a=(int*)malloc(sizeof(int)*n);
        if(a==NULL){
            printf("memory allocation to a(%d) failed\n",i);
            exit(1);
        }
        input_iarray(s4,a,c,N4);
//        printf("%d\n",c);
//        print_ivector(a,c);
        free(s4);
        *(ars+i*(N2+1))=c;
        for(int j=1;j<=c;j++){
            *(ars+i*(N2+1)+j)=*(a+j-1);
        }
        for(int j=c+1;j<(N2+1);j++){
            *(ars+i*(N2+1)+j)=-1;
        }
        free(a);

    }
    printf("(begin debug output)\n");
    for(int i=0;i<n;i++){
        printf("(debug output): loop i=%d\n",i);
        for(int j=0;j<(N2+1);j++){
            printf("%d ",*(ars+i*(N2+1)+j));
        }
        printf("\n");
    }
    printf("(end debug output)\n");
    char *s5=(char*)malloc(sizeof(char)*N3);
    if(s5==NULL){
        printf("memory allocation to s5 failed\n");
        exit(1);
    }
    char *ts5=fgets(s5,N3,stdin);
    if(ts5==NULL){
        printf("fgets(s5) failed\n");
        exit(1);
    }
    int x=readint(s5);
    free(s5);
    printf("(debug output): ");
    printf("x=%d\n",x);
    int min=N2,ans_idx=0;
    int* ans=(int*)malloc(sizeof(int)*n);
    int* flgs=(int*)malloc(sizeof(int)*n);
    if(ans==NULL){
        printf("memory allocation to ans failed\n");
        exit(1);
    }
    if(flgs==NULL){
        printf("memory allocation to flgs failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        *(ans+i)=-1;
        *(flgs+i)=0;
    }
    for(int i=0;i<n;i++){
        int flag=0;
        for(int j=1;j<*(ars+i*(N2+1));j++){
            if(*(ars+i*(N2+1)+j)==x){
                flag=1;
//                printf("%d was found in line (%d)\n",*(ars+i*(N2+1)+j),i);
                break;
            }
        }
        if(flag==1 && *(ars+i*(N2+1))<min){
            min=*(ars+i*(N2+1));
//            printf("min is changed to %d\n",min);
        }
        if(flag==1){
            *(flgs+i)=1;
        }
    }
    printf("(debug output): ");
    printf("min=%d\n",min);
    for(int i=0;i<n;i++){
        if(*(flgs+i)==1 && *(ars+i*(N2+1))==min){
            *(ans+ans_idx)=i+1; ans_idx++;
//            printf("i=%d\n");
        }
    }
    printf("%d\n",ans_idx);
    ans_idx=0;
    while(ans_idx<n && *(ans+ans_idx)!=-1){
        printf("%d",*(ans+ans_idx));
        if(ans_idx<n-1 && *(ans+ans_idx+1)!=-1)putchar(32);
        ans_idx++;
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

まず、① n*38のサイズのメモリ ars を取って、与えられたn組の各データを、行の先頭にcを格納し、続く部分に二行目の配列を格納して、残りの部分があればそこを-1で埋めた配列の形で保持し、次に、② xを含み行の最も小さい要素数minを求め、最後に、③ xを含み要素数が min である行番号の配列を作っている。(ansも最初に全部を-1で埋めているが、出力は-1でない実要素が入った部分のみとしている。)

このコードは、入力例の二つの例ではACになるのだが、他の大部分ではWAになる。

例えば、テストケース000.txt

1
37
21 36 33 9 28 1 0 34 3 25 13 31 20 2 22 4 11 15 7 18 6 32 5 14 26 35 12 10 24 17 23 29 19 30 8 27 16
36
    
では、
(begin debug output)
(debug output): loop i=0
37 21 36 33 9 28 1 0 34 3 25 13 31 20 2 22 4 11 15 7 18 6 32 5 14 26 35 12 10 24 17 23 29 19 30 8 27 16 
(end debug output)
(debug output): x=0
(debug output): min=37
1
1
という出力となり、この場合たまたま解答は正解と一致しているのだが、本来36であるはずのxが0になる。

更に、上のテストケースでn=2にして、

2
37
21 36 33 9 28 1 0 34 3 25 13 31 20 2 22 4 11 15 7 18 6 32 5 14 26 35 12 10 24 17 23 29 19 30 8 27 16
1
36
36
としてみると、
(begin debug output)
(debug output): loop i=0
37 21 36 33 9 28 1 0 34 3 25 13 31 20 2 22 4 11 15 7 18 6 32 5 14 26 35 12 10 24 17 23 29 19 30 8 27 16 
(debug output): loop i=1
0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 
(end debug output)
fgets(s5) failed
となって、2組目のデータが正しく読み込まれていないうえ、xの読み込みのfgetsが失敗している。

1組目のデータの読み込みの最後のところに問題があるのではないかと推測したが、それがなんであるのか分からず、StackOverflowで質問した。

結果は、なんと、配列aのメモリを確保した際に、イント型c個のメモリを取らなければいけないところ、cではなくnと書いていたのが原因だった。

指摘されてみれば拍子抜けするような単純なミスだったのだが、それに自分で気付かなかったのは事実。そう言えば、前にも変数名の書き間違いでコードがうまく動かず、原因がなかなか分からずに悩んだことがあった。

回答を頂いたactorbugさんからは、ローカルのデバッグ環境無しでCを使うのは無謀だと言われたが、確かにこういったミスに気付くための仕組みはないといけないかもしれないと思い始める。

さて、そこを直しても、上の修正テストケースで正しい結果が得られない。この場合

1
2
    
とならなければいけないのに
1
1
となってしまう。

目的のxが何行目の左から何番目に見つかったかも分かるようにデバッグ出力に手を加えてみながら(114行目)考えて、ここは j<*(ars+i*(N2+1)) ではなく、j<=*(ars+i*(N2+1)) としなければならないことに気付く。そうしないと、例えば要素数が1の場合、j=1から始めて j<1までの繰り返しになるので、ループが1回も回らないまま終わってしまう。どうも、(N2+1)に引きずられて、既に行の要素数の上限に1を足しているからその1個手前までループを回せばいいと勘違いしてしまったようだ。

そこを直して、最終的にACになったのが以下のコード。

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
#define N2 37
#define N3 4
#define N4 102
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);
    int *ars=(int*)malloc(sizeof(int)*n*(N2+1));
    if(ars==NULL){
        printf("memory allocation to ars failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char *s3=(char*)malloc(sizeof(char)*N3);
        if(s3==NULL){
            printf("memory allocation to s3(%d) failed\n",i);
            exit(1);
        }
        char *ts3=fgets(s3,N3,stdin);
        if(ts3==NULL){
            printf("fgets(s3(%d)) failed\n",i);
            exit(1);
        }
        int c=readint(s3);
        free(s3);
        char *s4=(char*)malloc(sizeof(char)*N4);
        if(s3==NULL){
            printf("memory allocation to s4(%d) failed\n",i);
            exit(1);
        }
        char *ts4=fgets(s4,N4,stdin);
        if(ts4==NULL){
            printf("fgets(s4(%d)) failed\n",i);
            exit(1);
        }
        int *a=(int*)malloc(sizeof(int)*c);
        if(a==NULL){
            printf("memory allocation to a(%d) failed\n",i);
            exit(1);
        }
        input_iarray(s4,a,c,N4);
//        printf("%d\n",c);
//        print_ivector(a,c);
        free(s4);
        *(ars+i*(N2+1))=c;
        for(int j=1;j<=c;j++){
            *(ars+i*(N2+1)+j)=*(a+j-1);
        }
        for(int j=c+1;j<(N2+1);j++){
            *(ars+i*(N2+1)+j)=-1;
        }
        free(a);

    }
/*    printf("(begin debug output)\n");
    for(int i=0;i<n;i++){
        printf("(debug output): loop i=%d\n",i);
        for(int j=0;j<(N2+1);j++){
            printf("%d ",*(ars+i*(N2+1)+j));
        }
        printf("\n");
    }
    printf("(end debug output)\n");*/
    char *s5=(char*)malloc(sizeof(char)*N3);
    if(s5==NULL){
        printf("memory allocation to s5 failed\n");
        exit(1);
    }
    char *ts5=fgets(s5,N3,stdin);
    if(ts5==NULL){
        printf("fgets(s5) failed\n");
        exit(1);
    }
    int x=readint(s5);
    free(s5);
    //printf("(debug output): ");
    //printf("x=%d\n",x);
    int min=N2,ans_idx=0;
    int* ans=(int*)malloc(sizeof(int)*n);
    int* flgs=(int*)malloc(sizeof(int)*n);
    if(ans==NULL){
        printf("memory allocation to ans failed\n");
        exit(1);
    }
    if(flgs==NULL){
        printf("memory allocation to flgs failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        *(ans+i)=-1;
        *(flgs+i)=0;
    }
    for(int i=0;i<n;i++){
        int flag=0;
        for(int j=1;j<=*(ars+i*(N2+1));j++){
            if(*(ars+i*(N2+1)+j)==x){
                flag=1;
//                printf("%d was found in line (%d), from left (%d)\n",*(ars+i*(N2+1)+j),i+1,j);
                break;
            }
        }
        if(flag==1 && *(ars+i*(N2+1))<min){
            min=*(ars+i*(N2+1));
//            printf("min is changed to %d\n",min);
        }
        if(flag==1){
            *(flgs+i)=1;
        }
    }
//    printf("(debug output): ");
//    printf("min=%d\n",min);
    for(int i=0;i<n;i++){
        if(*(flgs+i)==1 && *(ars+i*(N2+1))==min){
            *(ans+ans_idx)=i+1; ans_idx++;
//            printf("i=%d\n");
        }
    }
    printf("%d\n",ans_idx);
    ans_idx=0;
    while(ans_idx<n && *(ans+ans_idx)!=-1){
        printf("%d",*(ans+ans_idx));
        if(ans_idx<n-1 && *(ans+ans_idx+1)!=-1)putchar(32);
        ans_idx++;
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

ABC318-A

問題概要

数列 a[i]=m+p*i (i=1,2,…) の要素のうち、n以下であるものはいくつあるか。

解答

n,m,p=gets.chomp.split(" ").map(&:to_i)
if m<=n
    puts (n-m)/p+1
else
    puts 0
end    

a[i]がnを超えるまでループを回すのも芸がないので上のようなコードにした。場合分けをしたのは、例えばn=2,m=5,p=1のとき、(n-m)/p+1は-3となるように負の値になってしまうことがあるため。

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

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 22
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 i=0;
    while(*(s1+i)!=32)i++;
    i++;
    int m=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int p=readint(s1+i);
    free(s1);
//    printf("n=%d m=%d p=%d\n",n,m,p);
    int ans=-1;
    if(n>=m){
        ans=(n-m)/p+1;
    }else{
        ans=0;
    }
    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;
}
    

ABC321-A

問題概要

与えられた数字の各桁の数が左から見て狭義単調減少になっているか判定せよ。

解答

n=gets.to_i
s=n.to_s.split("").map(&:to_i)
(1...s.size).each do |i|
    if s[i]>=s[i-1]
        puts "No"
        exit
    end
end
puts "Yes"
    

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

#include
#include
#define N 7
int readint(char *s);
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 i=1;
    while(*(s+i)>=48 && *(s+i)<=57){
        if(*(s+i)>=*(s+i-1)){
            printf("No\n");
            exit(0);
        }
        i++;
    }
    printf("Yes\n");
    free(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;
}
    

ABC321-B

問題概要

n-1 個の整数からなる配列が与えられる。これにもう一つの整数を加えてできる配列を a とする。配列 a の最大値と最小値を一つずつ(最大値、最小値が複数ある場合には、それぞれについて一つのみが取り除かれる)取り除いた後の各要素の和をx以上とするための最後に加えるべき整数の最小値を出力せよ。

解答

n,x=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
a.sort!
sum1=sum2=0
(1..(n-3)).each do |i|
    sum1+=a[i]
end
sum2=sum1+a[n-2]
if sum1>=x
    puts 0
elsif sum1<x && sum2>=x
    if x-sum1<=100
        ans=x-sum1
        if ans==a[n-2]
            puts 0
        else
            puts ans
        end
    end
else
    puts -1
end
    

上が本番で提出したコードなのだが、ACが38個、WAが8個となった。

新たにCで書きながら、改めて考えてみる。

32 50 70
というような入力例を考え、最小値をmin, 真ん中の値をmid, 最大値をmaxとする。また、この段階での配列の総和は sum=152 である。この状態で、最大値と最小値を除いた総和は、mid=50 である。

ここで、最後に加えられた値(yとする)がmin以下だったとすると、新たにyがminとなり、総和は mid=50 に min=32 を足し戻した 82 になる。だからこの場合、sum-max が x 以上であればよいことになる。逆に言えば、sum-max が x 以上であれば、新たに加える値がmin以下の値であれば何でもよいことになる。よってこの場合には、配列の要素の取りうる最小値である0を答えればよい。

次に、yがmin以上でmax以下である場合を考える。この場合、従来のmaxとminが和から取り除かれることは変わらず、新たな配列の和は (sum-min-max)+y となる。よって、これが x 以上であればよい。逆に言えば、条件を満たす y の最小値は、x-(sum-min-max) である。max以下のこのようなyが存在すればよい。

最後に、yがmax以上である場合。この場合には、和から取り除かれるのは y であり、従前の和 mid=50 に max=70 が足し戻され、和は120 になる。つまり、新たな和は sum-min となる。この場合、最後に加えられる値は max 以上であれば何でもいいので、その中の最小値であるmaxを答えることになるが、最後の値がmaxに等しい場合は上の第二のケースに含めて考えることができ、独立して場合を立てる必要はない。

それをコード化してACになったのが、以下。

#include<stdio.h>
#include<stdlib.h>
#define N 10
#define N2 397
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
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 i=1;
    int n=readint(s);
    while(*(s+i)>=48 && *(s+i)<=57){
        i++;
    }
    i++;
    int x=readint(s+i);
    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);
    }
    int* a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n-1,N2);
    int min=100,max=0,sum=0,ans=0;
    for(i=0;i<n-1;i++){
        if(*(a+i)<min){
            min=*(a+i);
        }
        if(*(a+i)>=max){
            max=*(a+i);
        }
        sum+=*(a+i);
    }
//    printf("n=%d x=%d\n",n,x);
//    print_ivector(a,n-1);
//    printf("min=%d max=%d sum=%d\n",min,max,sum);
    if(sum-max>= x){
        ans=0;
    }else if (x-(sum-min-max) <= max){
        ans=x-(sum-min-max);
    }else {
        ans=-1;
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

ABC324-A

問題概要

与えられた配列の全ての項が等しいか判定せよ。

解答

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
(1...n).each do |i|
    if a[i]!=a[0]
        puts "No"
        exit
    end
end    

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

#include<stdio.h>
#include<stdlib.h>
#define N 5
#define N2 401
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
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);
    }
    int* a=(int*)malloc(sizeof(int)*n);
    if(a==NULL){
        printf("memory allocation to a failed\n");
        exit(1);
    }
    input_iarray(s2,a,n,N2);
    for(int i=1;i<n;i++){
        if(*(a+i)!=*a){
            printf("No\n");
            exit(0);
        }
    }
    //printf("n=%d\n",n);
    //print_ivector(a,n);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        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;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
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));
}
    

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


文字列の最後の文字

ABC244-A

問題概要

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

解答

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

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

#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;
}
    

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

ABC249-B

問題概要

与えられた文字列が、次の条件を満たすか否か判定せよ。

  1. 英大文字が少なくとも一つある。
  2. 英小文字が少なくとも一つある
  3. 全ての文字が相異なる

以下が本番の提出コード

s=gets.chomp
flag=true
if s.downcase==s
    flag=false
end
if s.upcase==s
    flag=false
end
s=s.split("").to_a
su=s.uniq
if s.size!=su.size
    flag=false
end
if flag==true
    puts "Yes"
else
    puts "No"
end
    

問題の条件は、以下の命題が全て成り立つことと同値である。

  1. 与えられた文字列の文字を全て全て大文字に変換したものが元の文字列に一致しない。
  2. 与えられた文字列の文字を全て全て小文字に変換したものが元の文字列に一致しない。
  3. 与えられた文字列から重複する文字を取り除いた文字列の長さが、元の文字列と同じである

本番ではこの置き換えた条件を使ってコードを書いたわけだが、より素直に、問題で与えられた条件をそのまま使ってコードを書いてもいいだろう。すると次のようになる。

s=gets.chomp.split("")
if !s.any?{|char| char.ord>=65 && char.ord<=90}
    puts "No"
    exit
end
if !s.any?{|char| char.ord>=97 && char.ord<=122}
    puts "No"
    exit
end
ref=Array.new(123,false)
s.each do |char|
    if ref[char.ord]==true
        puts "No"
        exit
    end
    ref[char.ord]=true
end
puts "Yes"        
    

「同じ文字が繰り返されていないか」の判定部分では、文字のアスキーコードをインデックスとする配列を作り、全てfalseで初期化。文字列を前から見ていって、出てきた対応するアスキーコードの所をtrueに。もしその文字のところがすでにtrueになっていたら、先にその文字が出てきていたということなので、"No"を出力して終了している。

ところで、「少なくとも1つが小文字」ではない⇔「全てが大文字」であるから、以下のようにしても論理的には等価なはずなのだが、なぜかこちらはうまく動かない。

if s.all?{|char| char.ord>=65 && char.ord<=90}
puts "No"
exit
end
if s.all?{|char| char.ord>=97 && char.ord<=122}
puts "No"
exit
end    

Cで書いたのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 101
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int flag1=0;
    int flag2=0;
    int flag3=1;
    for (int i=0;i<N;i++){
        if ((int)*(s+i)>=65 && (int)*(s+i)<=90)flag1=1;
        else if ((int)*(s+i)>=97 && (int)*(s+i)<=122)flag2=1;
        else break;
        for(int j=i+1;j<N;j++){
            if((!*(s+j)>=65 && *(s+j)<=90) || (!*(s+j)>=98 && *(s+j)<=122))break;
            else if (*(s+j)==*(s+i)){
                flag3=0;
                break;
            }
        }
        if(flag3==0){
            break;
        }
    }
    if (flag1==1 && flag2==1 && flag3==1)printf("Yes\n");
    else printf("No\n");
    return 0;
}
    

文字列の検索・操作、文字列についての条件判定、アスキーコード、正規表現(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;
}