これは良いインタビューの質問です.
キーポイント
重要なポイントは 2 つあります。
c1
1 文字は;としてエンコードする必要があります。
- エンコードされた長さは常に元の配列よりも小さくなります。
1 以降、各文字をエンコードするには少なくとも 2 つの場所が必要であることがわかっています。つまり、単一の文字のみがより多くのスペースをエンコードする必要があります。
シンプルなアプローチ
重要なポイントから、単一の文字がエンコード中に多くの問題を引き起こすことがわかります。エンコードされた文字列を保持するのに十分な場所がない可能性があるからです。では、最初にそれらを残し、最初に他の文字を圧縮するのはどうですか?
たとえば、aaaaabcddddee
最初に 1 文字を残して後ろからエンコードすると、次のようになります。
aaaaabcddddee
_____a5bcd4e2
次に、十分なスペースがあるようにキー ポイント 2 を指定すると、部分的にエンコードされたシーケンスを最初から安全にエンコードすることができます。
分析
解決策が見つかったようですが、これで終わりですか? いいえ、次の文字列を検討してください。
aaa3dd11ee4ff666
この問題は文字の範囲を制限しないので、数字も使用できます。この場合、同じアプローチを引き続き使用すると、次のようになります。
aaa3dd11ee4ff666
__a33d212e24f263
では、ランレングスと元の文字列の数値をどのように区別しますか?
さて、何か他のことを試す必要があります。
Encode Benefit (E)を次のように定義しましょう:エンコードされたシーケンスと元の連続した文字シーケンスの間の長さの差。.
たとえば、aa
has はE = 0
にaa
エンコードされるためa2
、長さの違いはありません。aaa
としてE = 1
エンコードされa3
、エンコードされたものとオリジナルの長さの差は です1
。1 文字のケースを見てみましょうE
。はい、-1
です。E
定義から、 :の式を推測できますE = ori_len - encoded_len
。
では、問題に戻りましょう。キーポイント 2 から、エンコードされた文字列は常に元の文字列よりも短くなることがわかっています。E
このキーポイントをどのように言い換えればよいでしょうか?
非常に単純です:はsigma(E_i) >= 0
、i番目の連続する文字部分文字列の です。E_i
Encode Benefit
たとえば、問題で指定したサンプル:aaaaabcddddee
は、次の 5 つの部分に分けることができます。
E(0) = 5 - 2 = 3 // aaaaa -> a5
E(1) = 1 - 2 = -1 // b -> b1
E(2) = 1 - 2 = -1 // c -> c1
E(3) = 4 - 2 = 2 // dddd -> d4
E(4) = 2 - 2 = 0 // ee -> e2
シグマは次のようになります3 + (-1) + (-1) + 2 + 0 = 3 > 0
。これは、エンコード後に 3 つのスペースが残ることを意味します。
ただし、この例から、潜在的な問題が見られる可能性があります。合計を行っているため、最終的な答えが 0 より大きい場合でも、途中でマイナスになる可能性があります。
はい、これは問題であり、非常に深刻です。E
が下回った場合0
、これは現在の文字をエンコードするのに十分なスペースがなく、その後の文字を上書きすることを意味します。
しかし、しかし、なぜ最初のグループから合計する必要があるのでしょうか? 負の部分をスキップするために途中から合計を開始できないのはなぜですか? 例を見てみましょう:
2 0 -1 -1 -1 1 3 -1
-1
最初から合計すると、インデックス 4 (0 ベース) で3 番目を追加すると 0 を下回ります。インデックス 5 から合計し、最後に到達したときにインデックス 0 にループバックする場合、問題はありません。
アルゴリズム
分析により、アルゴリズムに関する洞察が得られます。
- 最初から開始し
E
、現在の連続するグループを計算し、合計に追加しE_total
ます。
E_total
がまだ負でない場合(>= 0)、問題はなく、安全に次のグループに進むことができます。
E_total
が 0 を下回った場合、現在の位置からやり直す必要があります。つまり、クリアE_total
して次の位置に進みます。
シーケンスの最後に到達してE_total
もまだ負でない場合、最後の開始点は良い開始点です! このステップにはO(n)
時間がかかります。通常はループバックして再度確認する必要がありますが、ポイント 2 以降、確実に有効な答えが得られるので、ここで安全に終了できます。
次に、開始点に戻って従来のランレングス エンコーディングを開始できます。最後に到達したら、シーケンスの最初に戻って最初の部分を終了する必要があります。注意が必要なのは、文字列の末尾にある残りのスペースを使用する必要があることです。その後、順序の問題が発生した場合に備えてシフトを行う必要があり、余分な空白を削除すると、最終的に完了です:)
したがって、解決策があります (コードは疑似であり、検証されていません)。
// find the position first
i = j = E_total = pos = 0;
while (i < s.length) {
while (s[i] == s[j]) j ++;
E_total += calculate_encode_benefit(i, j);
if (E_total < 0) {
E_total = 0;
pos = j;
}
i = j;
}
// do run length encoding as usual:
// start from pos, end with len(s) - 1, the first available place is pos
int last_available_pos = runlength(s, pos, len(s)-1, pos);
// a tricky part here is to make use of the remaining spaces from the end!!!
int fin_pos = runlength(s, 0, pos-1, last_available_pos);
// eliminate the white
eliminate(s, fin_pos, pos);
// update last_available_pos because of elimination
last_available_pos -= pos - fin_pos < 0 ? 0 : pos - fin_pos;
// rotate back
rotate(s, last_available_pos);
複雑
アルゴリズムには 4 つの部分があります。
- 開始場所を見つける:
O(n)
- 文字列全体に対する Run-Length-Encoding:
O(n)
- 空白の除去:
O(n)
- その場での文字列の回転:
O(n)
したがってO(n)
、合計で持っています。
視覚化
この文字列をエンコードする必要があるとします。abccdddefggggghhhhh
最初のステップとして、開始位置を見つける必要があります。
Group 1: a -> E_total += -1 -> E_total = -1 < 0 -> E_total = 0, pos = 1;
Group 2: b -> E_total += -1 -> E_total = -1 < 0 -> E_total = 0, pos = 2;
Group 3: cc -> E_total += 0 -> E_total = 0 >= 0 -> proceed;
Group 4: ddd -> E_total += 1 -> E_total = 1 >= 0 -> proceed;
Group 5: e -> E_total += -1 -> E_total = 0 >= 0 -> proceed;
Group 6: f -> E_total += -1 -> E_total = -1 < 0 -> E_total = 0, pos = 9;
Group 7: ggggg -> E_total += 3 -> E_total = 3 >= 0 -> proceed;
Group 8: hhhhh -> E_total += 3 -> E_total = 6 >= 0 -> end;
したがって、開始位置は 9 になります。
v this is the starting point
abccdddefggggghhhhh
abccdddefg5h5______
^ last_available_pos, we need to make use of these remaining spaces
abccdddefg5h5a1b1c2
d3e1f1___g5h5a1b1c2
^^^ remove the white space
d3e1f1g5h5a1b1c2
^ last_available_pos, rotate
a1b1c2d3e1f1g5h5
最後の言葉
この質問は些細なことではなく、実際にはいくつかの伝統的なコーディング面接の質問を自然に結び付けています. 推奨されるマインド フローは次のようになります。
- パターンを観察し、重要なポイントを見つけます。
- スペースが不足している理由は、1 文字のエンコードが原因であることに気付きます。
- 連続する文字グループごとにエンコードの利点/コストを量子化します (別名Encoding Benefit )。
- 元のステートメントを説明するために提案した量子化を使用します。
- 適切な出発点を見つけるためのアルゴリズムを見つけます。
- 適切な出発点でランレングス エンコーディングを行う方法を理解する。
- エンコードされた文字列を回転させ、空白を削除する必要があることを理解してください。
- その場で文字列の回転を行うアルゴリズムを見つけます。
- その場で空白を削除するアルゴリズムを見つけてください。
正直なところ、インタビュー対象者が短時間でしっかりとしたアルゴリズムを考え出すのは少し難しいので、分析フローは非常に重要です。何も言わずに、あなたのマインドフローを見せてください。これは、面接官があなたの現在の段階を知るのに役立ちます.