2025年3月5日水曜日

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

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


逆順への並び替えと置換

ABC202-B

問題概要

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

解答

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

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

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

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

ABC211-B

問題概要

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

解答

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

if s==str then
    ans="Yes"
else
    ans="No"
end
puts ans
    

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

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

#include<stdio.h>
#include<stdlib.h>
#define N1 4
int main(void){
    int *ref=(int*)malloc(sizeof(int)*4);
    for(int i=0;i<4;i++)*(ref+i)=0;
    for(int i=0;i<4;i++){
        char* s1=(char*)malloc(sizeof(char)*N1);
        if (s1==NULL){
            printf("memory allocation to s1 failed.\n");
            exit(1);
        }
        char *ts1=fgets(s1,N1,stdin);
        if(ts1==NULL){
            printf("fgets(s1) failed\n");
            exit(1);
        }
//        printf("%s",s1);
        if(*s1==50 && *(s1+1)==66)*(ref+1)=1;
        else if(*s1==51 && *(s1+1)==66)*(ref+2)=1;
        else if(*s1==72 && *(s1+1)==82)*(ref+3)=1;
        else if(*s1==72)*ref=1;
    }
//    for(int i=0;i<4;i++)printf("%d ",*(ref+i));
    int flag=1;
    for(int i=0;i<4;i++){
        if(*(ref+i)==0){
            flag=0;
            break;
        }
    }
//    printf("flag=%d\n",flag);
    if(flag==1){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
    

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


文字列の一致判定

ABC215-A

問題概要

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

解答

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

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

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

int slen2(const char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 17
#define N2 12
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if (s1==NULL){
        printf("memory allocation to s1 failed.\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    char hello[]="Hello,World!";
    int size=slen2(s1);
/*    printf("s=%s\n",s1);
    printf("size of s=%d\n",size);
    printf("hello=%s\n",hello);
    printf("size of hello=%d\n",slen2(hello));*/
    if(size!=N2){
        printf("WA\n");
        exit(0);
    }
    for(int i=0;i<N2;i++){
        if(*(s1+i)!=*(hello+i)){
            printf("WA\n");
            exit(0);
        }
    }
    printf("AC\n");
    return 0;
}
int slen2(const char *s){
    int i=0;
    while((*(s+i)>=97 && *(s+i)<=122) || (*(s+i)>=65 && *(s+i)<=90) || *(s+i)==44 || *(s+i)==33)i++;
    return i;
}
    


文字列の辞書順比較

ABC217-A

問題概要

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

解答

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

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

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

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

含まれていない文字列

ABC217-B

問題概要

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

解答

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

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

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

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

文字列のi番目の文字

ABC218-A

問題概要

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

解答

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

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

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

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 3
#define N2 9
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if (s1==NULL){
        printf("memory allocation to s1 failed.\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int n=readint(s1);
    char* s2=(char*)malloc(sizeof(char)*N2);
    if (s2==NULL){
        printf("memory allocation to s2 failed.\n");
        exit(1);
    }
    char *ts2=fgets(s2,N2,stdin);
    if(ts2==NULL){
        printf("fgets(s2) failed\n");
        exit(1);
    }
    if(*(s2+n-1)==111){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

文字列の連結

ABC219-B

問題概要

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

解答

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

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

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

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

ABC221-B

問題概要

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

解答

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

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

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

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

辞書順で最大と最小

ABC223-B

問題概要

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

解答

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

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

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

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

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

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

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

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

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

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

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

ABC224-A

問題概要

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

解答

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

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

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

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

ABC230-B

問題概要

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

解答

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

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

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

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

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

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


カエサル暗号

ABC232-B

問題概要

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

解答

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

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

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

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

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


文字列の反転

ABC233-B

問題概要

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

解答

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

puts s1.join("") 
   

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

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

#include<stdio.h>
#include<stdlib.h>
#define N1 15
#define N2 1e5+2
int readint(char *s);
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if (s1==NULL){
        printf("memory allocation to s1 failed.\n");
        exit(1);
    }
    char *ts1=fgets(s1,N1,stdin);
    if(ts1==NULL){
        printf("fgets(s1) failed\n");
        exit(1);
    }
    int l=readint(s1);
    int i=0;
    while(*(s1+i)!=32)i++;
    i++;
    int r=readint(s1+i);
    char* s=(char*)malloc(sizeof(char)*N2);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N2,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    l--;r--;
    while(l<r){
        char temp=*(s+l);
        *(s+l)=*(s+r);
        *(s+r)=temp;
        l++;r--;
    }
    printf("%s\n",s);
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

文字の入れ替え

ABC236-A

問題概要

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

解答

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

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

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

文字のソート

ABC242-B

問題概要

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

解答

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

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

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

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

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


文字列の最後の文字

ABC244-A

問題概要

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

解答

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

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

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

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

ABC248-A

問題概要

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

解答

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

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

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

#define N1 11
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    fgets(s1,N1,stdin);
    for(char i=48;i<=57;i++){
        int flag=0;
        for(int j=0;j<9;j++){
            if(*(s1+j)==i){
                flag=1;
                break;
            }
        }
        if(flag==0){
            printf("%c\n",i);
            exit(0);
        }
    }
    return 0;
}
    

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

0 件のコメント:

コメントを投稿