文字列を全ての組み合わせで2つに分割する

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
山崎

文字列を全ての組み合わせで2つに分割する

#1

投稿記事 by 山崎 » 16年前

毎度お世話になっております、山崎です。
今回は、文字列の分割についての方法をお伺いに参りました。

ある文字列Sを、考えられる全ての組み合わせで 文字列Aと文字列Bの2つの文字列に分割し、その文字列を得る
というプログラムを考えているのですが、何日考え続けても有効なプログラムが浮かんできません。

分割の条件としては、
・文字列Sは'A'から'Z'までの文字で構成され、同じ文字は2つ以上存在しない。長さは2文字以上。
・各文字列中の文字の並び順が違っても区別しない。"AC"と"CA"は同じもの。(常にA→Zの順でソートされる)
・文字列A,Bは1文字以上。

例えば文字列Sが"ABCD"だとします。
そうするとこれは、(文字列A,文字列B の順番で記述しています)
A,BCD   B,ACD   C,ABD   D,ABC
AB,CD   AC,BD   AD,BC   BC,AD   BD,AC   CD,AB
ABC,D   ABD,C   ACD,B   BCD,A
この14通りに分割することができます。

文字列Sが"AB"のときは
A,B と B,A の2通りが考えられます。

言うまでもないかもしれませんが、文字列Aが決まれば自動的に文字列Bも決まります。

私は以下のコードのように、文字列Aの文字数によってswitch文を分岐させ、
文字列Aがn文字のときはfor文のn重ループで文字列Sから1ループで1文字ずつ合計n個選びAを決める、
という方法を考えたのですが、この方法ではnが大きな数値のときに対応できません。
void GetStrA(char* S)
{
	for(int i=1;i<strlen(S);i++)
		switch(i)
		{
		case 1:
			for(...){
				//文字列Sから1文字選び文字列Aとする。
				//Aを保存する
			}
				break;
		case 2:
			for(...)
				for(...){
					//文字列Sから1文字ずつ選び文字列Aに加える
					//Aを保存する
				}
			break;
		case 3:
			for(...)
				for(...)
					for(...){
						//文字列Sから1文字ずつ選び文字列Aに加える
						//Aを保存する
					}
			break;
		case ...
			...
		}
}
なお、Sを分割して得た文字列A、Bの保存のやり方は特に指定はありません。
文字列型のvectorにpushする、というのが適当でしょうか。
Aが決まればBも自然に決まるので、ここではBを求める処理は記述していません。

どうすれば、文字列Sを上の条件を満たす考えられる全ての組み合わせで2つに分割できるのでしょうか。

ねこ

Re:文字列を全ての組み合わせで2つに分割する

#2

投稿記事 by ねこ » 16年前

ぱっと見て答えてるので間違ってるかもしれません。

1.FOR文で1~文字数-1までループ
2.文字数分のパターン( 文字数x(文字数-1))ループ
  4文字なら1234,1243、1423等すべてのパターンを取得する

3.パターンに対応する文字をBYTE型で取得。ABCDで1234なら65,66,67,68(・・・だったかな)
4.Aのバイトコード(65)を引いてシフトする↑の例なら (1 << 0, 1 << 1, 1 << 2, 1 << 3)となる
5.5で求めた値をパイプでつなげる( 1,3分けなら「①と②|③|④」 )
6.A,B配列(ベクターでもなんでも良い)に5で計算した値( DWORD )を格納
7.格納する毎にA,Bに既に追加された値をチェックして同じ値なら追加しない
8.全パターンが格納されるので、取得時はビットから逆キャストして文字コードを取得する

4~6でビットにしているのはアルファベットが26個(32ビット未満)で
そのまま保持しているとABCとBCA等を比較する際に何通りかのチェックが必要で手間だからです。
ビットにしておくとif( ( A == B )で済みます。
取得時は値に対して
for( int i = 0; i < 26; i++ ) {
if( ( 1 << i ) & 配列の値 ) {
char( 65 + i )をどっか追加
}
}

見直してみて自分でもすごく分かりにくい説明だと思いました。
他にもっと簡単なやり方知ってる方は僕のを無視して書き込んでやってくださいorz

たいちう

Re:文字列を全ての組み合わせで2つに分割する

#3

投稿記事 by たいちう » 16年前

こんな感じでいいんでない

int n = Sの長さ;
for (int i = 1; i < (1 << n) - 1; i++) {
for (int j = 0; j < n; j++) {
if (i & (1 << j))
S[j]をAに追加
else
S[j]をBに追加
}
AとBを表示
}

non

Re:文字列を全ての組み合わせで2つに分割する

#4

投稿記事 by non » 16年前

2つに分けた文字列で左側のグループに必ずAが含まれるとしたら、
14通りではなく
A,BCD   B,ACD   C,ABD   D,ABC
AB,CD   AC,BD   AD,BC   BC,AD   BD,AC   CD,AB
ABC,D   ABD,C   ACD,B   BCD,A
7通りですよね。
ですから、かならずAが含まれている文字列のみを表示するように作ってみました。
プログラムの説明は面倒なのでしません。自分で考えてね。
#include<stdio.h>
#include<string.h>

char str[/url]="ABCD";
char str1[100];
void disp(int n,int m,int z)
{
	int i;
	if(n==z-1)
		return;
	for(i=m+1;i<z;i++){
		str1[n]=str;
		str1[n+1]='\0';
		puts(str1);
		disp(n+1,i,z);
	}
}
int main(void)
{
	str1[0]=str[0];
	puts(str1);
	disp(1,0,strlen(str));
	return;
}

山崎

Re:文字列を全ての組み合わせで2つに分割する

#5

投稿記事 by 山崎 » 16年前

ねこさん、たいちうさん、nonさん、ご返答誠にありがとうございます。
おかげさまで、文字列の分割を実現することができました。

今回は、たいちうさんのコードを参考とさせて頂きました。
ねこさんとnonさんの方法も後ほど検討させて頂きますね。

たいちうさんのコードを見て大感激しました…!!
私が何日考えても思いつかなかったアルゴリズムを、
たった数行のコードで実現なさってしまいますとは…!
尊敬と同時に、私の腕の無さを実感いたしました。
これからも精進したいと思います。

ご返事くださった皆様、誠にありがとうございました。

たいちう

Re:文字列を全ての組み合わせで2つに分割する

#6

投稿記事 by たいちう » 16年前

そこまで書かれると少し照れるな。
コードを理解できて感激できるならば、山崎さんもなかなかです。
今後このパターンを応用できることでしょう。

# preタグを付け忘れて見にくくなってすみません。

閉鎖

“C言語何でも質問掲示板” へ戻る