バックグラウンド
ことわざにあるように、古いものを見直して新しいものを学びましょう。chatGPTの効果はすごい!それは単に粉砕の効果です。しかし、まだ希望はあります。まずそれを掴み、それからイノベーションを起こしましょう。まず理解してから、それを超えてください。
ps: 前回に向けてアルゴリズムの質問アイデアをブラッシュアップします。さて、chatGPT3.5ベースのビッグモデルの魅力を感じてください。
文字列の基本
C/C++ の各文字列は「\0」で終わるため、文字列の最後を見つけるのに非常に便利です。この機能により、各文字列には余分な文字のオーバーヘッドがあり、少し不注意になると文字列が範囲外になってしまいます。以下のように、0123456789 を配列 str に正しくコピーするには、長さ 11 バイトの配列を宣言する必要があります。
char str[10];
strcpy(str,"0123456789");
文字列配列と文字列ポインタ
メモリを節約するために、C/C++ は定数文字列を別のメモリ領域に置きます。複数のポインタが同じ定数文字列に割り当てられている場合、それらは実際には同じメモリ アドレスを指します。しかし、定数メモリで初期化された配列の場合は状況が異なります。
次のコードを実行すると、結果はどうなるでしょうか?
int main()
{
char str1[]="hello world";
char str2[]="hello world";
char* str3="hello world";
char* str4="hello world";
if(str1==str2)
printf("str1 and str2 are same.\n");
else
printf("str1 and str2 are not same.\n");
if(str3==str4)
printf("str3 and str4 are same.\n");
else
printf("str3 and str4 are not same.\n");
return 0;
}
str1 と str2 は 2 つの文字列配列で、長さ 12 バイトのスペースを 2 つ割り当て、「hello world」の内容をそれぞれの配列にコピーします。これらは初期アドレスが異なる 2 つの配列であるため、str1 と str2 は異なる値になります。
str3 と str4 は 2 つのポインタです。文字列の内容を格納するためにメモリを割り当てる必要はなく、メモリ内の「hello world」のアドレスを指すだけで済みます。「hello world」は定数文字列であるため、メモリ内にはそのコピーが 1 つだけ存在するため、str3 と str4 は同じ値になります。
アルゴリズムの質問: スペースを置換する
質問: 文字列内の各スペースを「%20」に置き換える関数を実装してください。たとえば、「We are happy.」と入力し、「We%20are%20happy.」と出力します。
アイデア: このトピックを見て、最初に元のスペース文字について考えます。これは置換後に '%'、'2'、'0' の 3 文字になるため、文字列が長くなります。元の文字列に対して置換が実行されると、文字列の背後にあるメモリが上書きされ、変更される可能性があります。新しい文字列を作成し、それを新しい文字列に置き換えると、十分なメモリを自分で割り当てることができます。したがって、まず質問者のニーズを明確にする必要があります。元の文字列を置換する場合は、入力文字列の背後に十分な空きメモリがあることを確認してください。
時間計算量が O(n^2) の解
これを行う最も直感的な方法は、文字列を最初から最後までスキャンし、スペース文字が見つかるたびに置き換えることです。1 文字が 3 文字に置き換えられるため、後続の文字をすべて 2 バイト戻す必要があります。そうしないと、2 文字が上書きされます。文字列の長さが n であるとします。スペース文字ごとに O(n) 個の文字を移動する必要があるため、O(n) 個のスペースを含む文字列の場合、合計の時間効率は O(n^2) になります。
時間計算量が O(n) の解
まず文字列を 1 回走査し、文字列内のスペースの合計数を数えます。次に、文字列の後ろからコピーと置換を開始します。ポインタはp1とp2の2つを用意します。P1 は元の文字列の末尾を指し、P2 は置換された文字列の末尾を指します。
次に、ポインタ P1 を前方に移動し、最初のスペースが見つかるまで、ポインタ P1 が指す文字を P2 が指す位置に 1 文字ずつコピーします。最初のスペースに遭遇した後、p1 を 1 スペース前に移動し、文字列 "%20" を P2 の前に挿入します。"%20" の長さは 3 なので、同時に P2 を 3 スペース前に移動します。このようにして、P1 は 2 番目のスペースに遭遇するまで前方にコピーされます。P1 と P2 が同じ位置を指している場合、すべてのスペースが置き換えられたことを意味します。
このアルゴリズムのすべての文字は 1 回だけコピー (移動) されるため、このアルゴリズムの時間計算量は O(n) です。
/*length 为字符数组str的总容量,大于或等于字符串str的实际长度*/
void ReplaceBlank(char str[], int length)
{
if(str == nullptr && length <= 0)
return;
/*originalLength 为字符串str的实际长度*/
int originalLength = 0;
int numberOfBlank = 0;
int i = 0;
while(str[i] != '\0')
{
++ originalLength;
if(str[i] == ' ')
++ numberOfBlank;
++ i;
}
/*newLength 为把空格替换成'%20'之后的长度*/
int newLength = originalLength + numberOfBlank * 2;
if(newLength > length)
return;
int indexOfOriginal = originalLength;
int indexOfNew = newLength;
while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
{
if(str[indexOfOriginal] == ' ')
{
str[indexOfNew --] = '0';
str[indexOfNew --] = '2';
str[indexOfNew --] = '%';
}
else
{
str[indexOfNew --] = str[indexOfOriginal];
}
-- indexOfOriginal;
}
}
関連トピック: 2 つのソートされた配列がソートされ、一方の配列にマージされる
説明: 2 つのソートされた配列 A1 と A2。A1 の最後には A2 を保持するのに十分な空き領域があります。A2のすべての数値をA1に挿入し、すべての数値がソートされる関数を実装してください。
答えのアイデア:
A1 と A2 を比較して前から後ろに挿入すると、コピー番号が複数存在します。A1 と A2 の番号を末尾から先頭に比較し、大きい方の番号を A1 の適切な場所にコピーするのが良い方法です。これにより、移動回数が減り、効率が向上します。
イースターエッグ: chatGPT アルゴリズム
同じ問題が chatGPT3.5 にも投げかけられ、最初に実装する最も簡単なアイデアが与えられ、その後 2 回の手動リマインダーの後、最適化が続けられ、上で述べた最適な解決策が得られました。
chatGPT の回答へのコメント:
chatGPT の最初の 2 ラウンドの答えは正しいです。しかし、3回戦の答えは不正解。
たとえば、arr1[i] が arr2[j] より大きいことが判明した場合、arr1 が後方に移動する回数を減らすために、arr1[i+1:] の要素を意図的に前に移動させません。正しいですが、arr1[i+ 1:] から開始します arr2[j] 以下の数値を見つけます。ここで、arr1 は順序付けされた配列であるため、後続の数値は前の数値より小さくすることはできません。明らかに、chatGPT のロジック混乱しています。
実際、3 回目の最適化では、ネットワークが安定していないため、chatGPT は 3 つの応答を再生成しました。上記は 3 回目に生成された比較的完全な応答を示していますが、その前の 2 つの応答のプロセスを記録しました。完全に書かれていますが、アイデアは上で述べたダブル ポインター、つまり 2 つの配列を後ろから前に比較して挿入する方法です。何度も答えを再生成するように要求したため、誤解を与え、答えがよく考えられていないと思われた可能性があります。
最初の 2 回はネットワークの問題により中断され、回答の半分しか生成されませんでした。次のように:
初代の答え
2 番目に生成された答え:
これを見てどう思いますか?