221 lines
7.6 KiB
C#
221 lines
7.6 KiB
C#
namespace AdminService.BeautifulNumber;
|
|||
|
|
|
||
|
|
public class BeautifulNumberGenerator
|
||
|
|
{
|
||
|
|
public static List<string> GenerateAllPossibleNumbersForRule(string rule)
|
||
|
|
{
|
||
|
|
var parts = ParseRuleIntoParts(rule);
|
||
|
|
int totalLength = parts.Sum(p => p.Length);
|
||
|
|
if (totalLength != 8)
|
||
|
|
{
|
||
|
|
string details = string.Join(", ", parts.Select(p => $"'{p.OriginalString}'({p.Type}, {p.Length}位)"));
|
||
|
|
throw new ArgumentException($"规则 '{rule}' 解析后的总长度为 {totalLength} 位,不符合要求的8位。详情: [{details}]");
|
||
|
|
}
|
||
|
|
|
||
|
|
var partCombinations = new List<List<string>>();
|
||
|
|
foreach (var part in parts)
|
||
|
|
{
|
||
|
|
partCombinations.Add(GenerateCombinationsForPart(part));
|
||
|
|
}
|
||
|
|
|
||
|
|
var finalNumbers = CartesianProduct(partCombinations);
|
||
|
|
return finalNumbers.Distinct().ToList();
|
||
|
|
}
|
||
|
|
|
||
|
|
private class RulePart
|
||
|
|
{
|
||
|
|
public string OriginalString { get; set; }
|
||
|
|
public PartType Type { get; set; }
|
||
|
|
public int Length { get; set; }
|
||
|
|
public string UniqueLetters { get; set; }
|
||
|
|
public bool HasPlaceholder { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
private enum PartType
|
||
|
|
{
|
||
|
|
Sequential,
|
||
|
|
FixedGroup, // K, T, KK, TT, KKT, TTKK etc.
|
||
|
|
Individual, // X
|
||
|
|
FixedNumber // 8888
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<RulePart> ParseRuleIntoParts(string rule)
|
||
|
|
{
|
||
|
|
var parts = new List<RulePart>();
|
||
|
|
int i = 0;
|
||
|
|
while (i < rule.Length)
|
||
|
|
{
|
||
|
|
char c = rule[i];
|
||
|
|
|
||
|
|
if (char.IsDigit(c))
|
||
|
|
{
|
||
|
|
int start = i;
|
||
|
|
while (i < rule.Length && char.IsDigit(rule[i])) i++;
|
||
|
|
parts.Add(new RulePart { OriginalString = rule.Substring(start, i - start), Type = PartType.FixedNumber, Length = i - start });
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c >= 'A' && c <= 'H')
|
||
|
|
{
|
||
|
|
int start = i;
|
||
|
|
int direction = 0;
|
||
|
|
if (i + 1 < rule.Length && char.IsUpper(rule[i + 1]) && (rule[i + 1] >= 'A' && rule[i + 1] <= 'H'))
|
||
|
|
{
|
||
|
|
direction = rule[i + 1] > c ? 1 : -1;
|
||
|
|
i++;
|
||
|
|
while (i + 1 < rule.Length && char.IsUpper(rule[i + 1]) && (rule[i + 1] >= 'A' && rule[i + 1] <= 'H') && rule[i + 1] - rule[i] == direction)
|
||
|
|
{
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
bool hasPlaceholder = (i + 1 < rule.Length && rule[i + 1] == 'X');
|
||
|
|
if (hasPlaceholder) i++;
|
||
|
|
|
||
|
|
int sequenceLength = i - start + 1;
|
||
|
|
parts.Add(new RulePart { OriginalString = rule.Substring(start, sequenceLength), Type = PartType.Sequential, Length = sequenceLength, HasPlaceholder = hasPlaceholder });
|
||
|
|
i++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c == 'X')
|
||
|
|
{
|
||
|
|
int start = i;
|
||
|
|
while (i < rule.Length && rule[i] == 'X') i++;
|
||
|
|
parts.Add(new RulePart { OriginalString = rule.Substring(start, i - start), Type = PartType.Individual, Length = i - start });
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c == 'K' || c == 'T')
|
||
|
|
{
|
||
|
|
int start = i;
|
||
|
|
while (i < rule.Length && (rule[i] == 'K' || rule[i] == 'T')) i++;
|
||
|
|
string pattern = rule.Substring(start, i - start);
|
||
|
|
string uniqueLtrs = new string(pattern.Distinct().OrderBy(ch => ch).ToArray());
|
||
|
|
parts.Add(new RulePart { OriginalString = pattern, Type = PartType.FixedGroup, Length = pattern.Length, UniqueLetters = uniqueLtrs });
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
return parts;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<string> GenerateCombinationsForPart(RulePart part)
|
||
|
|
{
|
||
|
|
switch (part.Type)
|
||
|
|
{
|
||
|
|
case PartType.Sequential:
|
||
|
|
return GenerateSequentialNumbersWithPlaceholder(part.OriginalString, part.HasPlaceholder);
|
||
|
|
case PartType.FixedGroup:
|
||
|
|
return GenerateFixedGroupNumbers(part.OriginalString, part.UniqueLetters);
|
||
|
|
case PartType.Individual:
|
||
|
|
return GenerateIndividualNumbers(part.OriginalString.Length);
|
||
|
|
case PartType.FixedNumber:
|
||
|
|
return new List<string> { part.OriginalString };
|
||
|
|
default:
|
||
|
|
return new List<string>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<string> GenerateSequentialNumbersWithPlaceholder(string seqRule, bool hasPlaceholder)
|
||
|
|
{
|
||
|
|
string sequenceRule = hasPlaceholder ? seqRule.Substring(0, seqRule.Length - 1) : seqRule;
|
||
|
|
int length = sequenceRule.Length;
|
||
|
|
if (length == 0) return new List<string> { "" };
|
||
|
|
|
||
|
|
var baseSequences = new List<string>();
|
||
|
|
bool isAscending = length > 1 && sequenceRule[1] > sequenceRule[0];
|
||
|
|
|
||
|
|
if (isAscending)
|
||
|
|
{
|
||
|
|
int maxStart = 10 - length;
|
||
|
|
for (int start = 0; start <= maxStart; start++)
|
||
|
|
{
|
||
|
|
string num = "";
|
||
|
|
for (int i = 0; i < length; i++) num += (start + i).ToString();
|
||
|
|
baseSequences.Add(num);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
int minStart = length - 1;
|
||
|
|
for (int start = 9; start >= minStart; start--)
|
||
|
|
{
|
||
|
|
string num = "";
|
||
|
|
for (int i = 0; i < length; i++) num += (start - i).ToString();
|
||
|
|
baseSequences.Add(num);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!hasPlaceholder)
|
||
|
|
{
|
||
|
|
return baseSequences;
|
||
|
|
}
|
||
|
|
|
||
|
|
var finalSequences = new List<string>();
|
||
|
|
var digits = new List<string> { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||
|
|
foreach (var seq in baseSequences)
|
||
|
|
{
|
||
|
|
foreach (var digit in digits)
|
||
|
|
{
|
||
|
|
finalSequences.Add(seq + digit);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return finalSequences;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<string> GenerateFixedGroupNumbers(string pattern, string uniqueLetters)
|
||
|
|
{
|
||
|
|
var allCombinations = new List<List<string>>();
|
||
|
|
foreach (char letter in uniqueLetters)
|
||
|
|
{
|
||
|
|
var letterValues = new List<string>();
|
||
|
|
for (int i = 0; i < 10; i++) letterValues.Add(i.ToString());
|
||
|
|
allCombinations.Add(letterValues);
|
||
|
|
}
|
||
|
|
|
||
|
|
var letterAssignments = CartesianProduct(allCombinations);
|
||
|
|
var results = new List<string>();
|
||
|
|
foreach (var assignment in letterAssignments)
|
||
|
|
{
|
||
|
|
var map = new Dictionary<char, string>();
|
||
|
|
for (int i = 0; i < uniqueLetters.Length; i++)
|
||
|
|
{
|
||
|
|
map[uniqueLetters[i]] = assignment[i].ToString();
|
||
|
|
}
|
||
|
|
|
||
|
|
string number = "";
|
||
|
|
foreach (char c in pattern)
|
||
|
|
{
|
||
|
|
number += map[c];
|
||
|
|
}
|
||
|
|
results.Add(number);
|
||
|
|
}
|
||
|
|
return results;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<string> GenerateIndividualNumbers(int length)
|
||
|
|
{
|
||
|
|
var allCombinations = new List<List<string>>();
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
var digitList = new List<string>();
|
||
|
|
for (int j = 0; j < 10; j++) digitList.Add(j.ToString());
|
||
|
|
allCombinations.Add(digitList);
|
||
|
|
}
|
||
|
|
return CartesianProduct(allCombinations);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<string> CartesianProduct(List<List<string>> listOfLists)
|
||
|
|
{
|
||
|
|
IEnumerable<string> result = new[] { "" };
|
||
|
|
foreach (var list in listOfLists)
|
||
|
|
{
|
||
|
|
result = from r in result
|
||
|
|
from l in list
|
||
|
|
select r + l;
|
||
|
|
}
|
||
|
|
return result.ToList();
|
||
|
|
}
|
||
|
|
}
|