在编程领域,生成特定类型的字符串或数字序列是一个常见的任务。特别是对于二进制回文,即正读和反读都一样的二进制字符串,存在多种生成方法。本文将介绍一种高效的算法,该算法专注于直接构造所需的第n个二进制回文,而无需构造任何其他回文。
二进制回文的构造可以通过将每个回文分为左右两部分来实现,因为一个部分应该是另一个部分的反向。例如,对于一个偶数位的回文,中间的分隔符可以是 "|",用于可视化分隔。
以下是根据位数将连续的二进制回文分组的示例:
0
1
---------------Group 0------------------
1|1
101
111
---------------Group 1------------------
10|01
11|11
10001
10101
11011
11111
---------------Group 2------------------
100|001
101|101
110|011
111|111
1000001
1001001
1010101
1011101
1100011
1101011
1110111
1111111
如上所示,组0包含2位和3位的回文,组1包含4位和5位的回文,依此类推。
为了构造第n个二进制回文,首先需要确定它属于哪个组。这里可以使用二进制对数来计算:
groupIndex = floor(log2((nth / 3) + 1))
除以3是因为每个组可以被分成3个子组:没有中间位、中间位为0、中间位为1。接下来,可以计算每个子组中的元素数量:
subGroupCount = 2 ^ groupIndex
然后,计算当前组之前的所有二进制回文数量(使用几何级数求和公式的变体):
Sum = ((2 ^ groupIndex) - 1) * 3
现在可以计算目标回文在其组内的索引:
var insideGroupIndex = nth - Sum;
以及在子组内的索引:
var insideSubGroupIndex = insideGroupIndex % subGroupCount;
接下来,可以确定二进制回文的中间位是空的、0还是1(这是子组的索引):
var opType = (int)Math.Floor((double)insideGroupIndex / subGroupCount);
现在有了组装回文所需的所有信息,但需要根据不同的子组进行不同的组装:
if (opType == 0)
left side of a binary palindrome should be "1" + binaryStringRepresentation(subGroupIndex) padded left to digits count in a group. There should be no connector between sides
if (opType == 1)
left side of a binary palindrome should be "1" + binaryStringRepresentation(subGroupIndex / 2) padded left to digits count in a group. Connector should be '0' or '1' based on if groupIndex is divisible by 2 (even or odd)
if (opType == 2)
left side of a binary palindrome should be "1" + binaryStringRepresentation((subGroupIndex / 2) + (subGroupCount / 2)) padded left to digits count in a group. Connector should be '0' or '1' based on if groupIndex is divisible by 2 (even or odd)
最终输出应该是:
output = leftSide + connector + reverse(leftSide)
private string GetNthBinaryPalindrome(int n)
{
switch (n)
{
case 1:
return "0";
case 2:
return "1";
case 3:
return "11";
case 4:
return "101";
case 5:
return "111";
default:
if (n < 1)
return null;
var S = n - 3;
var groupIndex = (int)Math.Floor(Math.Log((S / 3) + 1, 2));
var digitsCount = groupIndex + 1;
var subGroupCount = (int)Math.Round(Math.Pow(2, groupIndex));
var Sn = (subGroupCount - 1) * 3;
var insideGroupIndex = S - Sn;
var subGroupIndex = insideGroupIndex % subGroupCount;
var opType = (int)Math.Floor((double)insideGroupIndex / subGroupCount);
string leftSide;
string connector;
switch (opType)
{
case 0:
connector = "";
leftSide = "1" + Convert.ToString(subGroupIndex, 2).PadLeft(digitsCount, '0');
break;
case 1:
connector = ((groupIndex & 1) == 0) ? "0" : "1";
leftSide = "1" + Convert.ToString(subGroupIndex / 2, 2).PadLeft(digitsCount, '0');
break;
case 2:
connector = ((groupIndex & 1) == 0) ? "0" : "1";
leftSide = "1" + Convert.ToString(subGroupIndex / 2 + subGroupCount / 2, 2).PadLeft(digitsCount, '0');
break;
default:
return null;
}
return String.Format("{0}{1}{2}", leftSide, connector, String.Join("", leftSide.Reverse()));
}
}