Количество комбинаций чисел при данных условиях

Как найти количество комбинаций чисел из строки от 4 до 12 символов?

Условие:

количество разделяющих точек - 3, символов между точками от 1 до 3, цифры местами не меняются.

Например:

Строка '1234'. 
Возможные комбинации: 
'1.2.3.4'
Количество комбинаций = '1.

Строка '12345'. 
Возможные комбинации:
'1.2.3.45',
'1.2.34.5',
'1.23.4.5',
'12.3.4.5'
Количество комбинаций = '4'

Ответы (3 шт):

Автор решения: MBo

Рекурсивный подсчёт. n - длина строки, k - количество частей, m - максимум между точками.

Сначала уменьшаем размерность задачи, вычтя 1 из m и всех частей (т.о. пустые части возможны).

Потом подставляем возможные размеры очередной части и рекурсивно решаем для оставшихся частей

Код (спасибо CbIPoK2513 за перевод с Python) выдаёт 19 - проверяем:
6 перестановок 3 3 2 2
12 перестановок 3 2 2 1
1 вариант 2 2 2 2

function cp(n, k, m) {
  if (k == 0)
    if (n == 0) return 1;
    else return 0;
  res = 0;
  for (let i = 0; i < Math.min(n + 1, m + 1); i++)
    res += cp(n - i, k - 1, m);
  return res;
}

function countparts(n, k, m) {
  return cp(n - k, k, m - 1);
}

console.info(countparts(8, 4, 3));

→ Ссылка
Автор решения: MaxLevs

Самый простой способ - алгоритм перебора с возвратом.

Фишка в том, чтобы перебрать все состояния и отобрать из них те, которые подходят под условия задачи. Состояние здесь - массив вида [1,1,0,1,...], где 1 - точка стоит, и 0 - точка не стоит для каждого "паза" между символами.

const data = "0123456789";
const points_number = 3;
const data_length = data.length - 1;

const max_state_index = 1;
// 2 states: 0 - there isn't a point, 1 - there is a point
const EMPTY_SPACE_STATE_INDEX = 0;
const POINT_STATE_INDEX = 1;

let results = [];

function loop(index, limitIndex, loopLimit, currentState, callback) {
    for (let j = 0; j <= loopLimit; ++j) {
        currentState[index] = j;
        if (index !== limitIndex - 1) {
            loop(index + 1, limitIndex, loopLimit, currentState, callback);
        } else {
            callback(currentState);
        }
    }
}

function startLoop(limitIndex, loopLimit, callback) {
    loop(0, limitIndex, loopLimit, [], callback);
}

function isCorrectState(state) {
    let point_counter = 0;
    let last_point_index = -1;
    for (let shard of state) {
        if (shard === POINT_STATE_INDEX) {
            ++point_counter;
        }
    }
    if (point_counter !== points_number) {
        return false;
    }

    for (let j = 0; j < state.length; ++j) {
        if (state[j] === POINT_STATE_INDEX) {
            if (last_point_index >= 0) {
                let distance = Math.abs(j - last_point_index);
                const metric = points_number;
                if (distance > metric) {
                    return false;
                }
            }
            last_point_index = j;
        }
    }

    return true;
}

function calculateNewState(state) {
    const isCorrect = isCorrectState(state);
    if (isCorrect) {
        results.push([...state]);
    }
}

function printString(data, state) {
    let res = "";
    for (let i = 0; i < data.length; ++i) {
        res += data[i] + (state[i] == POINT_STATE_INDEX ? "." : "");
    }
    console.log(res);
}


startLoop(data_length, max_state_index, calculateNewState);
for (let state of results) {
    printString(data, state);
}
console.log("Count: ", results.length);

Здесь константа data_length определяет длину входной строки. Её можно брать как stringData.length при вводе данных в виде строки.

Функция loop - цикл с переменной вложенностью.

Функция startLoop - функция для запуска цикла по переданным параметрам.

Функция isCorrectState - предикат определяющий, соответствует ли состояние условию задачи.

Функция calculateNewState - callback для переменного цикла, выбирает из новых состояний корректные и делает с ними что-нибудь (записывает в массив с результатами или считает.

→ Ссылка
Автор решения: vsemozhebuty

Наверное, это не совсем честный алгоритм (скорее шулерская реализация принципа, предложенного MaxLevs), но пусть будет для коллекции.

Предлагаю такой порядок.

  1. Представить комбинации в виде числа в бинарном представлении, где ноль будет представлять точку, а единица символ строки.
  2. Получить всю последовательность чисел между минимальным вариантом (1_0_1_0_1_0_1) и максимальным (111_0_111_0_111_0_111).
  3. Убрать из этой последовательности все неподходящие комбинации (где нулей больше трёх, где число единиц между нулями меньше 1 и больше 3, где нули не между единицами).
  4. Распределить оставшуюся последовательность в группы по количеству единиц. Так мы получим объект массивов, где ключ — длина строки, а массив — все комбинации точек и символов в этой строке.
  5. Для удобства добавить ещё один объект, где где ключ — длина строки, а значение — количество комбинаций.

'use strict';

const min = 0b1_0_1_0_1_0_1;
const max = 0b111_0_111_0_111_0_111;

const validation = /^1{1,3}01{1,3}01{1,3}01{1,3}$/u;

const validCombinations =
  Array.from(
    Array(max - min + 1),
    (_, i) => (min + i).toString(2)
  )
  .filter(
    combination => validation.test(combination)
  )
  .reduce((acc, combination) => {
    const { length } = combination.replace(/0/ug, '');
    if (!acc[length]) acc[length] = [];
    acc[length].push(combination.replace(/0/ug, '.'));
    return acc;
  }, {});

const stringLengthToCombinationCount = Object.fromEntries(
  Object.entries(validCombinations).map(([key, value]) => [key, value.length])
);

console.log(stringLengthToCombinationCount);
console.log(validCombinations);

→ Ссылка