最速怪談選手権

以下のプログラムは(A|B)&(A|B|C)&(B|C)→(B)|(A&C)というような論理演算ができるのですが、アルファベット大文字の26文字限定です。大文字と小文字を別のものと区別して52文字判別するにはどこを変更すれば良いでしょうか。getExp内のelse if('A' <= *c && *c <= 'Z')をif('A' <= *c && *c <= 'z')のようにしても上手くいきませんでした

#include <stdio.h>
#define BUFSIZE_STR 1024 //バッファ(適当なサイズ)
#define BUFSIZE_EXP 1024 //バッファ(適当なサイズ)
#define BUFSIZE_ANS 1024 //バッファ(適当なサイズ)
char str[BUFSIZE_STR]; //入力された数式を格納
long exp[BUFSIZE_EXP]; //ビット配列化した数式を格納
long ans[BUFSIZE_ANS]; //ビット配列化した答えを格納
int expCount = 0; //ビット配列化した数式の数
int ansCount = 0; //ビット配列化した答えの数
int maxAlpha = 0; //使用されているアルファベットの最大

void getExp(void);
int checkExp(long x);
int addAnswer(long x);
void printAnswer(void);

void main(void)
{
long x;
getExp();
for(x = 1; x < (1<<maxAlpha); x++) if(checkExp(x)) if(addAnswer(x)) return;
printAnswer();
}

//標準入力から数式を入力しビット配列化する
void getExp(void)
{
int i, j, flg;
char *c;
puts("数式を入力して下さい 例 (A|B)&(A|C)");
fgets(str,BUFSIZE_STR,stdin);
for(c = str, flg = 0; *c != '\0'; c++){
if(!flg) exp[expCount] = 0;
if(*c == '&' && flg){
expCount++;
flg = 0;
}else if('A' <= *c && *c <= 'Z'){
if((*c - 'A' + 1) > maxAlpha) maxAlpha = *c - 'A' + 1;
exp[expCount] |= (1 << (int)(*c - 'A'));
flg = 1;
}
}
if(flg) expCount++;
}

//数式を評価する
int checkExp(long x)
{
int i;
for(i = 0; i < expCount; i++) if(!(exp[i] & x)) return 0;
return 1;
}

//吸収則で不要な答えを排除しながら、答えを登録
int addAnswer(long x)
{
int i;
for(i = 0; i < ansCount; i++){
if((ans[i] & x) == ans[i]) return 0; //吸収則により排除
if((ans[i] & x) == x){ ans[i] = x; return 0; } //吸収則により排除
}
//答えの登録
ans[ansCount++] = x;
if(ansCount >= BUFSIZE_ANS){ puts("バッファが足りません。"); return -1; }
return 0;
}

//ビット配列化されている答えを、アルファベットで表示
void printAnswer(void)
{
int i, j, flg;
for(j = 0; j < ansCount; j++){
putchar('(');
for(i = 0, flg = 0; i < maxAlpha; i++){
if(ans[j] & (1 << i)){
if(flg) putchar('&');
putchar('A'+i);
flg = 1;
}
}
putchar(')');
if(j < ansCount -1) putchar('|');
}
putchar('\n');
}

質問者からの補足コメント

  • 大文字だけでなく小文字も扱いたいため具体的に上手くいかないとは、本文と同じ内容で大文字小文字を入れ替えて(a|b)&(a|b|c)&(b|c)→(b)|(a&c)となるようにしたいのですが(B&b)|(A&C&a&c)となってしまい、また(a|B)&(a|B|C)&(b|c)のようにしても同様に(B&b)|(A&C&a&c)となってしまいます。

      補足日時:2021/06/29 19:10

A 回答 (10件)

unsigned long long を使わないのはなぜなんだろう>#9.



(long)1 もちょっとダサい.
    • good
    • 0

No6です。


>ご回答ありがとうございます。8が表示され64bitということが確認出来ました。実際に使えるようにしていただいたとのことなのですが拝見させていただいてもよろしいでしょうか

こちらで修正したソースです。
64ビット環境が必須です。
No6でも書きましたが、使えるのはa,b,c程度で、e,f,g,..zは使えないと思います。使用するアルファベットのコードが大きくなるごとに処理時間が劇的に長くなります。
main()内の
for (x = 1; x < ((long)1 << maxAlpha); x++){
が原因です。

-----------------------------------------
#include <stdio.h>
#define BUFSIZE_STR 1024 //バッファ(適当なサイズ)
#define BUFSIZE_EXP 1024 //バッファ(適当なサイズ)
#define BUFSIZE_ANS 1024 //バッファ(適当なサイズ)
char str[BUFSIZE_STR]; //入力された数式を格納
long exp[BUFSIZE_EXP]; //ビット配列化した数式を格納
long ans[BUFSIZE_ANS]; //ビット配列化した答えを格納
int expCount = 0; //ビット配列化した数式の数
int ansCount = 0; //ビット配列化した答えの数
int maxAlpha = 0; //使用されているアルファベットの最大

void getExp(void);
int checkExp(long x);
int addAnswer(long x);
void printAnswer(void);

void main(void)
{
long x;
getExp();
for (x = 1; x < ((long)1 << maxAlpha); x++){
if (checkExp(x)){
if (addAnswer(x)){
return;
}
}
}
int i;
printAnswer();
}

//標準入力から数式を入力しビット配列化する
void getExp(void)
{
int i, j, flg,ax;
char *c;
puts("数式を入力して下さい 例 (A|B)&(A|C)");
fgets(str, BUFSIZE_STR, stdin);
for (c = str, flg = 0; *c != '\0'; c++) {
if (!flg)
exp[expCount] = 0;
if (*c == '&' && flg) {
expCount++;
flg = 0;
} else if (('A' <= *c && *c <= 'Z') || ('a' <= *c && *c <= 'z')) {
if (*c <= 'Z'){
ax = *c - 'A';
}else{
ax = *c - 'a' + 26;
}
if (ax + 1> maxAlpha)
maxAlpha = ax + 1;
exp[expCount] |= ((long)1 << ax);
flg = 1;
}
}
if (flg)
expCount++;

}

//数式を評価する
int checkExp(long x)
{
int i;
for (i = 0; i < expCount; i++)
if (!(exp[i] & x))
return 0;
return 1;
}

//吸収則で不要な答えを排除しながら、答えを登録
int addAnswer(long x)
{
int i;
for (i = 0; i < ansCount; i++) {
if ((ans[i] & x) == ans[i])
return 0; //吸収則により排除
if ((ans[i] & x) == x) {
ans[i] = x;
return 0;
} //吸収則により排除
}
//答えの登録
ans[ansCount++] = x;
if (ansCount >= BUFSIZE_ANS) {
puts("バッファが足りません。");
return -1;
}
return 0;
}

//ビット配列化されている答えを、アルファベットで表示
void printAnswer(void)
{
int i, j, flg;
for (j = 0; j < ansCount; j++) {
putchar('(');
for (i = 0, flg = 0; i < maxAlpha; i++) {
if (ans[j] & ((long)1 << i)) {
if (flg)
putchar('&');
if (i < 26){
putchar('A' + i);
}else{
putchar('a' - 26 + i);
}
flg = 1;
}
}
putchar(')');
if (j < ansCount - 1)
putchar('|');
}
putchar('\n');
}
    • good
    • 0

ご提示の処理のままでは、あまり綺麗に実装できそうにないので、


以下のような拡張をお勧めします。

--

英字以外や単語への対応も視野に入れて、
アルファベットではなく変数名を扱うと考える

使用する変数名の一覧配列を用意する
例) char varNames[52];

exp と ans のビット配列を、
下位ビットから A,B,C ... のアルファベットと見なさずに、
一覧配列の 0,1,2 ... 番目の変数名と見なす

getExp で解析中に、
A-Z (U+0041 - U+005A) と、
a-z (U+0061 - U+007A) の文字を、変数名と見なす
未知の変数名を見つけたら、一覧配列に追加する

以下の関数を用意するとソースが読みやすくなり便利です
* 一覧配列を探し、変数名 から 番号 に変換する (exp へ代入用)
* 一覧配列を探し、番号 から 変数名 に変換する (ans を表示用)
    • good
    • 0
この回答へのお礼

回答ありがとうございます。ご提案参考にさせて頂きます

お礼日時:2021/07/04 12:48

>if('A' <= *c && *c <= 'Z')をif('A' <= *c && *c <= 'z')のようにしても上手くいきませんでした



私なら、*c が大文字と小文字なら番号0から51を返す、そうでなければ -1 を返す関数、例えば isAlpha() を定義して、
 if( isAlpha(*c) >=0 )
に置き換え、
putchar('A'+i);のように番号から大文字と小文字に変換する部分も、例えば num2Alpha(int i) のような関数を定義して、
 putchar(num2Alpha(i) );
に置き換えるかな。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。参考にさせて頂きます。

お礼日時:2021/07/04 12:48

前提条件として、64ビットの整数型が扱える環境が必要になります。


64ビットのLinux環境であれば、long型は64ビット整数型になるので
A-Z,a-zの文字をビットとして保持することが可能です。
まず、あなたの環境でlong型が4バイトなのか8バイトなのかを調査してください。

以下のプログラムを実行し、8が表示されればlong型は64ビット整数型になりますのでa-zの文字列を追加することは可能です。
しかしながら、実際にこちらでa-zの文字列を使えるように改修してみましたが、実質的に使用可能なのはa~dの文字のようです。e~zの文字を使用した場合は、結果がでるまでに非常に時間がかかり、使いものにならないかと思います。
まずは、あなたの環境でlong型のサイズを確認してください。
#include <stdio.h>
int main(void)
{
long a;
printf ("size of a=%d\n",sizeof(a));
return 0;
}
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。8が表示され64bitということが確認出来ました。実際に使えるようにしていただいたとのことなのですが拝見させていただいてもよろしいでしょうか

お礼日時:2021/07/04 12:51

No.1、3です。



文字コードの調整をしないと、
小文字の時のmaxAlphaは正常値よりも大きくなり、メインのループ数は正常回数ではなくなるでしょう。

exp配列に入る値も実際の文字コードとは異なるものが入るので、多分checkExpの結果も狂ってくると思います。
そうすると、addAnswerが走るタイミングもおかしくなると思います。

ans配列には正しくないアルファベットを、正しいコードで格納しますが、putchar('A'+i);で格納された文字とは別の文字を出力しているような気がします。
格納時と出力時のズレが同じなので、一見すると文字的には正しく出力しているように見えそうです。

私の頭では、紙にでも書きながら考えないと何が起きているか判断できませんが、この修正は、1箇所2箇所の修正で済むレベルのものでは無いと思いますよ。
    • good
    • 0

大文字と小文字を区別することにすると, アルファベットは全部で何文字あるんでしょうかね.

    • good
    • 0

No.1です。



少し下の方も見ましたが、
*c - 'A' のような、文字コードで計算している箇所が幾つかありますね。

これら全て同じようにズレが出てしまうと思いますよ。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。補足コメントの通り全て小文字入力すると大文字も含めてではありますが似たような答えが出てきており、文字がズレているというよりは大文字小文字が別の文字として扱われていないというような状況のためprintAnswerに問題があると考えています。

お礼日時:2021/06/29 22:02

「上手くいかない」というのは, 具体的にはどう「上手くいかない」んだろう.



アルファベットは文字コードが連続しているという保証がないので
'A' <= *c && *c <= 'Z'
ではうまく判定できない可能性があるんだけど, 理解できているのかなぁ....
    • good
    • 0

ざっとしか読んでいませんが、


}else if('A' <= *c && *c <= 'z'){ とした場合、

ここでおかしくなっていませんか?
if((*c - 'A' + 1) > maxAlpha)

アスキーコードのアルファベット大文字Zと小文字aの間には幾つか記号が入っています。

アスキーコード表
http://www3.nit.ac.jp/~tamura/ex2/ascii.html

例えば、'Z'-'A'は25ですが、'a'-'A'は32です。
一例ですが、大文字の時と小文字の時は処理を分けるのが良いと思います。

以下適当なので、調整して下さい。

if (*c<='Z'){
tmp_c=*c - 'A' + 1
}else{
tmp_c=*c - 'a' + 27
}

if(tmp_c > maxAlpha)
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!