Найти все возможные комбинации слов с дефисами и без дефисов

для строки, в которой может быть ноль или более дефисов, мне нужно извлечь все различные возможности с дефисами и без них.

например, строка "A-B" приведет к "A-B" и "AB" (две возможности).

строка "A-B-C" приведет к "A-B-C", "AB-C", "A-BC" и "ABC" (четыре возможности).

строка "A-B-C - D" приведет к "A-B-C-D", "AB-C-D", "A-BC-D", "A-B-CD", "AB-CD", "ABC-D", "A-BCD" и "ABCD" (восемь возможности.)

...и т. д. и т. п.

я экспериментировал с некоторыми вложенными циклами, но не смог приблизиться к желаемому результату. Я подозреваю, что мне нужно что-то рекурсивное, если нет простого решения, которое я упускаю.

NB. Это должно построить SQL-запрос (позор, что SQL Server не имеет соответствия шаблону регулярного выражения MySQL).

вот одна попытка, над которой я работал. Это может сработать, если я сделаю это. рекурсивно.

string keyword = "A-B-C-D";

List<int> hyphens = new List<int>();

int pos = keyword.IndexOf('-');
while (pos != -1)
{
    hyphens.Add(pos);
    pos = keyword.IndexOf('-', pos + 1);
}

for (int i = 0; i < hyphens.Count(); i++)
{
    string result = keyword.Substring(0, hyphens[i]) + keyword.Substring(hyphens[i] + 1);

    Response.Write("<p>" + result);
}

A B C D-слова различной длины.

5 ответов


вы должны иметь возможность отслеживать каждую позицию дефиса и в основном говорить ее либо там, либо нет. Пройдитесь по всем комбинациям, и вы получите все ваши строки. Я нашел самый простой способ отслеживать его с помощью двоичного файла, так как его легко добавить с Convert.ToInt32

Я придумал это:

string keyword = "A-B-C-D";
string[] keywordSplit = keyword.Split('-');
int combinations = Convert.ToInt32(Math.Pow(2.0, keywordSplit.Length - 1.0));

List<string> results = new List<string>();

for (int j = 0; j < combinations; j++)
{
    string result = "";
    string hyphenAdded = Convert.ToString(j, 2).PadLeft(keywordSplit.Length - 1, '0');
    // Generate string
    for (int i = 0; i < keywordSplit.Length; i++)
    {
        result += keywordSplit[i] +
                  ((i < keywordSplit.Length - 1) && (hyphenAdded[i].Equals('1')) ? "-" : "");
    }
    results.Add(result);
}

взгляните на ваши примеры. Вы заметили закономерность?

  • С 1 дефисом есть 2 возможности.
  • С 2 дефисами есть 4 возможности.
  • С 3 дефисами есть 8 возможностей.

количество возможностей равно 2n.

это буквально экспоненциальный рост, поэтому, если в строке слишком много дефисов, быстро станет невозможным распечатать их все. (С всего 30 дефисов - это более миллиарда комбинаций!)

тем не менее, для меньшего количества дефисов может быть интересно создать список. Для этого вы можете рассматривать каждый дефис как бит в двоичном числе. Если бит равен 1, дефис присутствует, в противном случае его нет. Таким образом, это предлагает довольно простое решение:

  1. разделить исходную строку на дефисы
  2. пусть n = число дефисов
  3. отсчет от 2n - 1 до 0. Рассматривайте этот счетчик как битовую маску.
  4. для каждого счета начните строить строку, начиная с первой части.
  5. объединить все оставшиеся детали в строку в порядке, предшествовать дефис, только если соответствующий бит в битовой маске находится.
  6. добавьте полученную строку к выходу и продолжайте, пока счетчик не будет исчерпан.

в переводе на код у нас есть:

public static IEnumerable<string> EnumerateHyphenatedStrings(string s)
{
    string[] parts = s.Split('-');
    int n = parts.Length - 1;
    if (n > 30) throw new Exception("too many hyphens");
    for (int m = (1 << n) - 1; m >= 0; m--)
    {
        StringBuilder sb = new StringBuilder(parts[0]);
        for (int i = 1; i <= n; i++)
        {
            if ((m & (1 << (i - 1))) > 0) sb.Append('-');
            sb.Append(parts[i]);
        }
        yield return sb.ToString();
    }
}

Скрипка: https://dotnetfiddle.net/ne3N8f


это работает для меня:

Func<IEnumerable<string>, IEnumerable<string>> expand = null;
expand = xs =>
{
    if (xs != null && xs.Any())
    {
        var head = xs.First();
        if (xs.Skip(1).Any())
        {
            return expand(xs.Skip(1)).SelectMany(tail => new []
            {
                head + tail,
                head + "-" + tail
            });
        }
        else
        {
            return new [] { head };
        }
    }
    else
    {
        return Enumerable.Empty<string>();
    }
};

var keyword = "A-B-C-D";

var parts = keyword.Split('-');

var results = expand(parts);

Я:

ABCD 
A-BCD 
AB-CD 
A-B-CD 
ABC-D 
A-BC-D 
AB-C-D 
A-B-C-D 

Я тестировал этот код и он работает, как указано в вопросе. Я сохранил строки в List<string>.

string str = "AB-C-D-EF-G-HI";
string[] splitted = str.Split('-');

List<string> finalList = new List<string>();

string temp = "";

for (int i = 0; i < splitted.Length; i++)
{
    temp += splitted[i];
}
finalList.Add(temp);
temp = "";

for (int diff = 0; diff < splitted.Length-1; diff++)
{
    for (int start = 1, limit = start + diff; limit < splitted.Length; start++, limit++)
    {
        int i = 0;
        while (i < start)
        {
            temp += splitted[i++];
        }
        while (i <= limit)
        {
            temp += "-";
            temp += splitted[i++];
        }
        while (i < splitted.Length)
        {
            temp += splitted[i++];
        }
        finalList.Add(temp);
        temp = "";
    }
}

Я не уверен, что ваш вопрос полностью определен (т. е. Может ли у вас быть что-то вроде A-BCD-EF-G-H?). Для" полностью " дефисных строк (A-B-C-D -...- Z), что-то вроде этого должно сделать:

string toParse = "A-B-C-D";
char[] toParseChars = toPase.toCharArray();
string result = "";
string binary;
for(int i = 0; i < (int)Math.pow(2, toParse.Length/2); i++) { // Number of subsets of an n-elt set is 2^n
    binary = Convert.ToString(i, 2);
    while (binary.Length < toParse.Length/2) {
        binary = "0" + binary;
    }
    char[] binChars = binary.ToCharArray();

    for (int k = 0; k < binChars.Length; k++) {
        result += toParseChars[k*2].ToString();
        if (binChars[k] == '1') {
            result += "-";
        }
    }
    result += toParseChars[toParseChars.Length-1];
    Console.WriteLine(result);
}

идея здесь заключается в том, что мы хотим создать двоичное слово для каждого возможного дефис. Итак, если у нас есть A-B-C-D (три дефиса), мы создаем двоичные слова 000, 001, 010, 011, 100, 101, 110, и 111. Обратите внимание, что если у нас есть N дефисов, нам нужно 2^n двоичных слов.

затем каждое слово карты к выходу, который вы хотите, вставив дефис, где у нас есть " 1 " в нашем слове (000 -> ABCD, 001 -> ABC-D, 010 -> AB-CD и т. д.). Я не тестировал код выше, но это, по крайней мере, один из способов решить проблему для полностью дефисных слов.

отказ от ответственности: я на самом деле не проверить код