なんて楽しい問題でしょう!
他のポスターは、最小のインデックスを返すことは正しいですが、実際にはそれよりも興味深いものです。
配列を循環として扱う場合 (つまり、最後を過ぎたら先頭に戻る)、関数は最小の辞書式サブシーケンスの開始インデックスを返します。
最小要素が 1 つだけの場合は、その要素が返されます。複数の要素が最小の場合、各最小要素の次の要素を比較します。
10
との入力の例{0, 1, 2, 1, 1, 1, 0, 0, 1, 0}
:
- インデックス 0、6、7、および 9 に、0 の 4 つの最小要素があります。
- これら 2 つの要素の後に 1 (0 と 7 の要素) が続き、2 つの要素の後に 0 が続きます (6 と 9 の要素)。配列は円形であることに注意してください。
- 0 は 1 より小さいので、6 と 9 の 0 のみを考慮します。
- これらのうち、6 から始まる 3 つの要素のシーケンスは「001」であり、9 からのシーケンスも「001」であるため、どちらも同等に最小限です。
- 4 つの要素のシーケンスを見ると、要素 6 以降から「0010」、要素 9 以降から「0012」があります。したがって、6 以降のシーケンスは小さくなり、6 が返されます。(私はこれが事実であることを確認しました)。
リファクタリングされ、コメントされたコードは次のとおりです。
int findStartOfMinimumSubsequence(int length, char circular_array[])
{
#define AccessWithOffset(index) circular_array[(index + offset) % length]
int indicesStillConsidered[length], count_left = length, indicator[length], minIndex = 0;
for (int index = 0; index < length; index++)
{
indicesStillConsidered[index] = index;
indicator[index] = 1;
}
// Keep increasing the offset between pairs of minima, until we have eliminated all of
// them or only have one left.
for (int offset = 0; count_left >= 2; offset++)
{
// Find the index of the minimal value for the next term in the sequence,
// starting at each of the starting indicesStillConsidered
minIndex = indicesStillConsidered[0];
for (int i=0; i<count_left; i++)
minIndex = AccessWithOffset(indicesStillConsidered[i])<AccessWithOffset(minIndex) ?
indicesStillConsidered[i] :
minIndex;
// Ensure that indicator is 0 for indices that have a non-minimal next in sequence
// For minimal indicesStillConsidered[i], we make indicator 0 1+offset away from the index.
// This prevents a subsequence of the current sequence being considered, which is just an efficiency saving.
for (int i=0; i<count_left; i++){
offsetIndexToSet = AccessWithOffset(indicesStillConsidered[i])!=AccessWithOffset(minIndex) ?
indicesStillConsidered[i] :
(indicesStillConsidered[i]+offset+1)%length;
indicator[offsetIndexToSet] = 0;
}
// Copy the indices where indicator is true down to the start of the l array.
// Indicator being true means the index is a minimum and hasn't yet been eliminated.
for (int count_before=count_left, i=count_left=0; i<count_before; i++)
if (indicator[indicesStillConsidered[i]])
indicesStillConsidered[count_left++] = indicesStillConsidered[i];
}
return count_left == 1 ? indicesStillConsidered[0] : minIndex;
}
使用例
言いにくいです、本当に。不自然な例: 文字の循環リストから、同じ長さの他のサブシーケンスよりも早く辞書に現れる最短のサブシーケンスのインデックスを返します (すべての文字が小文字であると仮定します)。