Как распечатать все возможные комбинации букв, которые может представлять данный номер телефона? - PullRequest
56 голосов
/ 26 февраля 2010

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

Вторая часть вопроса была похожа на то, что, если бы это был 12-значный международный номер? Как это повлияет на ваш дизайн.

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

Ответы [ 32 ]

40 голосов
/ 27 февраля 2010

В Python, итеративный:

digit_map = {
    '2': 'abc',
    '3': 'def',
    '4': 'ghi',
    '5': 'jkl',
    '6': 'mno',
    '7': 'pqrs',
    '8': 'tuv',
    '9': 'wxyz',
}

def word_numbers(input):
  input = str(input)
  ret = ['']
  for char in input:
    letters = digit_map.get(char, '')
    ret = [prefix+letter for prefix in ret for letter in letters]
  return ret

ret пока список результатов; изначально он заполняется одним элементом - пустой строкой. Затем, для каждого символа во входной строке, он ищет список букв, которые ему соответствуют, из словаря, определенного вверху. Затем он заменяет список ret каждой комбинацией существующего префикса и возможной буквы.

15 голосов
/ 01 октября 2012

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

import java.util.HashMap;
public class Solution {
    public ArrayList<String> letterCombinations(String digits) {
        ArrayList<String> res = new ArrayList<String>();
        ArrayList<String> preres = new ArrayList<String>();
        res.add("");

        for(int i = 0; i < digits.length(); i++) {
            String letters = map.get(digits.charAt(i));
            if (letters.length() == 0)
                continue;
            for(String str : res) {
                for(int j = 0; j < letters.length(); j++)
                    preres.add(str + letters.charAt(j));
            }
            res = preres;
            preres = new ArrayList<String>();
        }      
        return res;
    }

    static final HashMap<Character,String> map = new HashMap<Character,String>(){{
        put('1', "");
        put('2',"abc");
        put('3',"def");
        put('4',"ghi");
        put('5',"jkl");
        put('6',"mno");
        put('7',"pqrs");
        put('8',"tuv");
        put('9',"wxyz");
        put('0', "");
    }} ;
}

Я не уверен, как 12-значные международные номера влияют на дизайн.

Редактировать: международные номера также будут обрабатываться

4 голосов
/ 26 февраля 2010

В Java с использованием рекурсии:

import java.util.LinkedList;
import java.util.List;

public class Main {  
    // Number-to-letter mappings in order from zero to nine
    public static String mappings[][] = {
        {"0"}, {"1"}, {"A", "B", "C"}, {"D", "E", "F"}, {"G", "H", "I"},
        {"J", "K", "L"}, {"M", "N", "O"}, {"P", "Q", "R", "S"}, 
        {"T", "U", "V"}, {"W", "X", "Y", "Z"}
    };

    public static void generateCombosHelper(List<String> combos, 
            String prefix, String remaining) {
        // The current digit we are working with
        int digit = Integer.parseInt(remaining.substring(0, 1));

        if (remaining.length() == 1) {
            // We have reached the last digit in the phone number, so add 
            // all possible prefix-digit combinations to the list
            for (int i = 0; i < mappings[digit].length; i++) {
                combos.add(prefix + mappings[digit][i]);
            }
        } else {
            // Recursively call this method with each possible new 
            // prefix and the remaining part of the phone number.
            for (int i = 0; i < mappings[digit].length; i++) {
                generateCombosHelper(combos, prefix + mappings[digit][i], 
                        remaining.substring(1));
            }
        }
    }

    public static List<String> generateCombos(String phoneNumber) {
        // This will hold the final list of combinations
        List<String> combos = new LinkedList<String>();

        // Call the helper method with an empty prefix and the entire 
        // phone number as the remaining part.
        generateCombosHelper(combos, "", phoneNumber);

        return combos;
    }

    public static void main(String[] args) {
        String phone = "3456789";
        List<String> combos = generateCombos(phone);

        for (String s : combos) {
            System.out.println(s);
        }
    }
}
3 голосов
/ 26 февраля 2010

Очевидным решением является функция сопоставления цифры со списком клавиш, а затем функция, которая генерирует возможные комбинации:

Первое очевидно, второе более проблематично, поскольку у вас есть около 3 ^ комбинаций цифр, что может быть очень большим числом.

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

Более очевидными решениями будут вложенные циклы или рекурсия, которые менее элегантны, но, на мой взгляд, верны.

Еще одна вещь, о которой необходимо позаботиться, - это избежать проблем с масштабируемостью (например, сохранение возможностей в памяти и т. Д.), Поскольку речь идет о множестве комбинаций.

P.S. Другим интересным расширением вопроса будет локализация.

3 голосов
/ 27 февраля 2012

В C ++ (рекурсивно):

string pattern[] = {"0",".,!","ABC","DEF","GHI","JKL","MNO","PQRS","TUV","WXYZ"};
ofstream keyout("keypad.txt");
void print_keypad(char* str, int k, vector<char> patt, int i){
if(str[k] != '\0')
{
    int x = str[k] - '0';
    for(int l = 0; l < pattern[x].length(); l++)
    {
        patt[i] = pattern[x][l];
        print_keypad(str, k+1, patt, i+1);
    }
    keyout << endl;
}
else if(i == k)
{
    string st(patt.data());
    keyout << st << endl;
    return;
}
}

Эта функция может быть вызвана с 'k' и 'i' равными нулю.

Любой, кому требуется больше иллюстраций, чтобы понятьлогика, может комбинировать технику рекурсии со следующим выводом:

ADG
ADH
ADI

AEG
AEH
AEI

AFG
AFH
AFI


BDG
BDH
BDI

BEG
BEH
BEI

BFG
BFH
...
3 голосов
/ 28 июня 2014

В цифровых клавиатурах текст и цифры размещаются на одной клавише. Например, 2 имеет «ABC», если мы хотим написать что-нибудь, начинающееся с «A», нам нужно набрать клавишу 2 один раз. Если мы хотим набрать «B», дважды нажмите клавишу 2 и трижды введите «C». ниже изображение такой клавиатуры.

клавиатура http://d2o58evtke57tz.cloudfront.net/wp-content/uploads/phoneKeyboard.png

Учитывая клавиатуру, как показано на рисунке, и n-значный номер, перечислите все возможные слова, нажав эти цифры.

Например, если введено число 234, возможные слова, которые могут быть сформированы (в алфавитном порядке): Adg Adh Adi AEG AHE AHE AFG AFH AFI BDG BDH BDI BHE BEH BE BE BFG BFH BFI CDG CDH CDI CEG CEG CEH CFG CFH CFI

Давайте сначала сделаем некоторые вычисления. Сколько слов возможно с семью цифрами, каждая из которых представляет собой n букв? Для первой цифры у нас есть максимум четыре варианта, и для каждого варианта для первой буквы у нас есть максимум четыре варианта для второй цифры и так далее. Так что это простая математика, это будет O (4 ^ n). Поскольку клавиши 0 и 1 не имеют соответствующего алфавита, а многие символы имеют 3 символа, 4 ^ n будет верхней границей числа слов, а не минимальных слов.

Теперь давайте рассмотрим несколько примеров.

Для числа выше 234. Видите ли вы какой-либо шаблон? Да, мы замечаем, что последний символ всегда либо G, H, либо I, и когда он сбрасывает свое значение с I на G, цифра слева от него изменяется. Точно так же всякий раз, когда второй последний алфавит сбрасывает свое значение, третий последний алфавит получает изменения и так далее. Первый символ сбрасывается только один раз, когда мы сгенерировали все слова. Это можно посмотреть и с другого конца. То есть всякий раз, когда символ в позиции i изменяется, персонаж в позиции i + 1 проходит через все возможные символы и создает волновой эффект, пока мы не достигнем конца. Так как 0 и 1 не имеют никаких символов, связанных с ними. мы должны сломаться, так как для этих цифр итерации не будет.

Давайте возьмем второй подход, так как его будет легко реализовать с помощью рекурсии. Идем до конца и возвращаемся один за другим. Идеальное состояние для рекурсии. давайте поищем базовый вариант. Когда мы достигаем последнего символа, мы печатаем слово со всеми возможными символами для последней цифры и возвращаем. Простой базовый случай. Когда мы достигаем последнего символа, мы печатаем слово со всеми возможными символами для последней цифры и возвращаем. Простой базовый корпус.

Ниже приводится реализация C рекурсивного подхода для печати всех возможных слов, соответствующих n-значному входному номеру. Обратите внимание, что входной номер представлен в виде массива для упрощения кода.

#include <stdio.h>
#include <string.h>

// hashTable[i] stores all characters that correspond to digit i in phone
const char hashTable[10][5] = {"", "", "abc", "def", "ghi", "jkl",
                           "mno", "pqrs", "tuv", "wxyz"};

// A recursive function to print all possible words that can be obtained
// by input number[] of size n.  The output words are one by one stored
// in output[]
void  printWordsUtil(int number[], int curr_digit, char output[], int n)
{
    // Base case, if current output word is prepared
int i;
if (curr_digit == n)
{
    printf("%s ", output);
    return ;
}

// Try all 3 possible characters for current digir in number[]
// and recur for remaining digits
for (i=0; i<strlen(hashTable[number[curr_digit]]); i++)
{
    output[curr_digit] = hashTable[number[curr_digit]][i];
    printWordsUtil(number, curr_digit+1, output, n);
    if (number[curr_digit] == 0 || number[curr_digit] == 1)
        return;
}
}

// A wrapper over printWordsUtil().  It creates an output array and
// calls printWordsUtil()
void printWords(int number[], int n)
{
char result[n+1];
result[n] ='\0';
printWordsUtil(number, 0, result, n);
}

//Driver program
int main(void)
{
int number[] = {2, 3, 4};
int n = sizeof(number)/sizeof(number[0]);
printWords(number, n);
return 0;
}

Вывод:

adg adh adi aeg aeh aei afg afh afi bdg bdh bdi beg beh bei bfg bfh bfi cdg cdh cdi ceg ceh cei cfg cfh cfi

Сложность времени:

Сложность времени вышеприведенного кода составляет O (4 ^ n), где n - количество цифр во входном номере.

Ссылки:

http://www.flipkart.com/programming-interviews-exposed-secrets-landing-your-next-job-3rd/p/itmdxghumef3sdjn?pid=9788126539116&affid=sandeepgfg

2 голосов
/ 03 февраля 2019

Эта проблема похожа на эту проблему с leetcode. Вот ответ, который я отправил для этой проблемы на leetcode (для объяснения проверьте github и video ).

Итак, самое первое, что нам нужно, это какой-то способ удержания отображений цифры, и мы можем использовать карту для этого:

private Map<Integer, String> getDigitMap() {
        return Stream.of(
                new AbstractMap.SimpleEntry<>(2, "abc"),
                new AbstractMap.SimpleEntry<>(3, "def"),
                new AbstractMap.SimpleEntry<>(4, "ghi"),
                new AbstractMap.SimpleEntry<>(5, "jkl"),
                new AbstractMap.SimpleEntry<>(6, "mno"),
                new AbstractMap.SimpleEntry<>(7, "pqrs"),
                new AbstractMap.SimpleEntry<>(8, "tuv"),
                new AbstractMap.SimpleEntry<>(9, "wxyz"))
               .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, 
                                AbstractMap.SimpleEntry::getValue));
}

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

private String getDigitMappings(String strDigit, Map<Integer,String> digitMap) {
        int digit = Integer.valueOf(strDigit);
        return digitMap.containsKey(digit) ? digitMap.get(digit) : "";
}

Эту проблему можно решить с помощью обратного отслеживания, а решение для обратного отслеживания обычно имеет структуру, в которой сигнатура метода будет содержать: контейнер результатов, временные результаты, исходный источник с индексом и т. Д. Таким образом, структура метода будет иметь вид:

private void compute(List<String> result, StringBuilder temp, String digits, int start, Map<Integer, String> digitMap) {
       // Condition to populate temp value to result
       // explore other arrangements based on the next input digit
       // Loop around the mappings of a digit and then to explore invoke the same method recursively
       // Also need to remove the digit which was in temp at last so as to get proper value in temp for next cycle in loop
}

И теперь тело метода можно заполнить как (результат будет сохранен в списке, temp в строителе строк и т. Д.)

private void compute(List<String> result, StringBuilder temp, String digits, int start, Map<Integer, String> digitMap) {
        if(start >= digits.length()) { // condition
            result.add(temp.toString());
            return;
        }

        String letters = getDigitMappings(digits.substring(start, start + 1), digitMap); // mappings of a digit to loop around
        for (int i = 0; i < letters.length(); i++) {
            temp.append(letters.charAt(i));
            compute(result, temp, digits, start+1, digitMap); //explore for remaining digits
            temp.deleteCharAt(temp.length() - 1); // remove last in temp
        }
}

И, наконец, метод может быть вызван как:

public List<String> letterCombinations(String digits) {
        List<String> result = new ArrayList<>();
        if(digits == null || digits.length() == 0) return result;
        compute(result, new StringBuilder(), digits, 0, getDigitMap());
        return result;
}

Теперь максимальные сопоставленные символы для цифры могут быть 4 (например, 9 имеет wxyz), а обратный поиск включает в себя исчерпывающий поиск, чтобы исследовать все возможные схемы (дерево пространства состояний), поэтому для цифры размера n мы будем иметь 4x4x4....n times то есть сложность будет O(4^n).

2 голосов
/ 19 октября 2014

в JavaScript. Класс CustomCounter заботится о приращении индексов. Затем просто выведите различные возможные комбинации.

var CustomCounter = function(min, max) {
    this.min = min.slice(0)
    this.max = max.slice(0)
    this.curr = this.min.slice(0)
    this.length = this.min.length
}

CustomCounter.prototype.increment = function() {
    for (var i = this.length - 1, ii = 0; i >= ii; i--) {
        this.curr[i] += 1
        if (this.curr[i] > this.max[i]) {
            this.curr[i] = 0
        } else {
            break
        }
    }
}

CustomCounter.prototype.is_max = function() {
    for (var i = 0, ii = this.length; i < ii; ++i) {
        if (this.curr[i] !== this.max[i]) {
            return false
        }
    }
    return true
}

var PhoneNumber = function(phone_number) {
    this.phone_number = phone_number
    this.combinations = []
}

PhoneNumber.number_to_combinations = {
    1: ['1']
  , 2: ['2', 'a', 'b', 'c']
  , 3: ['3', 'd', 'e', 'f']
  , 4: ['4', 'g', 'h', 'i']
  , 5: ['5', 'j', 'k', 'l']
  , 6: ['6', 'm', 'n', 'o']
  , 7: ['7', 'p', 'q', 'r', 's']
  , 8: ['8', 't', 'u', 'v']
  , 9: ['9', 'w', 'x', 'y', 'z']
  , 0: ['0', '+']
}

PhoneNumber.prototype.get_combination_by_digit = function(digit) {
    return PhoneNumber.number_to_combinations[digit]
}

PhoneNumber.prototype.add_combination_by_indexes = function(indexes) {
    var combination = ''
    for (var i = 0, ii = indexes.length; i < ii; ++i) {
        var phone_number_digit = this.phone_number[i]
        combination += this.get_combination_by_digit(phone_number_digit)[indexes[i]]
    }

    this.combinations.push(combination)
}

PhoneNumber.prototype.update_combinations = function() {
    var min_indexes = []
      , max_indexes = []

    for (var i = 0, ii = this.phone_number.length; i < ii; ++i) {
        var digit = this.phone_number[i]
        min_indexes.push(0)
        max_indexes.push(this.get_combination_by_digit(digit).length - 1)
    }

    var c = new CustomCounter(min_indexes, max_indexes)

    while(true) {
        this.add_combination_by_indexes(c.curr)
        c.increment()

        if (c.is_max()) {
            this.add_combination_by_indexes(c.curr)
            break
        }
    }
}

var phone_number = new PhoneNumber('120')
phone_number.update_combinations()
console.log(phone_number.combinations)
1 голос
/ 26 февраля 2010
namespace WordsFromPhoneNumber
{
    /// <summary>
    /// Summary description for WordsFromPhoneNumber
    /// </summary>
    [TestClass]
    public class WordsFromPhoneNumber
    {
        private static string[] Chars = { "0", "1", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ" };
        public WordsFromPhoneNumber()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        #region overhead

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void TestMethod1()
        {
            IList<string> words = Words(new int[] { 2 });
            Assert.IsNotNull(words, "null");
            Assert.IsTrue(words.Count == 3, "count");
            Assert.IsTrue(words[0] == "A", "a");
            Assert.IsTrue(words[1] == "B", "b");
            Assert.IsTrue(words[2] == "C", "c");
        }

        [TestMethod]
        public void TestMethod23()
        {
            IList<string> words = Words(new int[] { 2 , 3});
            Assert.IsNotNull(words, "null");
            Assert.AreEqual(words.Count , 9, "count");
            Assert.AreEqual(words[0] , "AD", "AD");
            Assert.AreEqual(words[1] , "AE", "AE");
            Assert.AreEqual(words[2] , "AF", "AF");
            Assert.AreEqual(words[3] , "BD", "BD");
            Assert.AreEqual(words[4] , "BE", "BE");
            Assert.AreEqual(words[5] , "BF", "BF");
            Assert.AreEqual(words[6] , "CD", "CD");
            Assert.AreEqual(words[7] , "CE", "CE");
            Assert.AreEqual(words[8] , "CF", "CF");
        }

        [TestMethod]
        public void TestAll()
        {
            int[] number = new int [4];
            Generate(number, 0);
        }

        private void Generate(int[] number, int index)
        {
            for (int x = 0; x <= 9; x += 3)
            {
                number[index] = x;
                if (index == number.Length - 1)
                {
                    var w = Words(number);
                    Assert.IsNotNull(w);
                    foreach (var xx in number)
                    {
                        Console.Write(xx.ToString());
                    }
                    Console.WriteLine(" possible words:\n");
                    foreach (var ww in w)
                    {
                        Console.Write("{0} ", ww);
                    }
                    Console.WriteLine("\n\n\n");
                }
                else
                {
                    Generate(number, index + 1);
                }
            }
        }

        #endregion

        private IList<string> Words(int[] number)
        {
            List<string> words = new List<string>(100);
            Assert.IsNotNull(number, "null");
            Assert.IsTrue(number.Length > 0, "length");
            StringBuilder word = new StringBuilder(number.Length);
            AddWords(number, 0, word, words);

            return words;
        }

        private void AddWords(int[] number, int index, StringBuilder word, List<string> words)
        {
            Assert.IsTrue(index < number.Length, "index < length");
            Assert.IsTrue(number[index] >= 0, "number >= 0");
            Assert.IsTrue(number[index] <= 9, "number <= 9");

            foreach (var c in Chars[number[index]].ToCharArray())
            {
                word.Append(c);
                if (index < number.Length - 1)
                {
                    AddWords(number, index + 1, word, words);
                }
                else
                {
                    words.Add(word.ToString());
                }
                word.Length = word.Length - 1;
            }
        }
    }
}
1 голос
/ 15 декабря 2015

Это порт C # этот ответ.

Код

public class LetterCombinations
{
    private static readonly Dictionary<string, string> Representations = new Dictionary<string, string>
    {
       {"2", "abc" },
       {"3", "def" },
       {"4", "ghi" },
       {"5", "jkl" },
       {"6", "mno" },
       {"7", "pqrs" },
       {"8", "tuv" },
       {"9", "wxyz" },
    };

    public static List<string> FromPhoneNumber(string phoneNumber)
    {
        var result = new List<string> { string.Empty };

        // go through each number in the phone
        for (int i = 0; i < phoneNumber.Length; i++)
        {
            var pre = new List<string>();
            foreach (var str in result)
            {
                var letters = Representations[phoneNumber[i].ToString()];
                // go through each representation of the number
                for (int j = 0; j < letters.Length; j++)
                {
                    pre.Add(str + letters[j]);
                }
            }
            result = pre;
        }

        return result;
    }
}

Модульные тесты

public class UnitTest
{
    [TestMethod]
    public void One_Digit_Yields_Three_Representations()
    {
        var sut = "2";

        var expected = new List<string>{ "a", "b", "c" };
        var actualResults = LetterCombinations.FromPhoneNumber(sut);

        CollectionAssert.AreEqual(expected, actualResults);
    }

    [TestMethod]
    public void Two_Digits_Yield_Nine_Representations()
    {
        var sut = "22";

        var expected = new List<string> { "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc" };
        var actualResults = LetterCombinations.FromPhoneNumber(sut);

        CollectionAssert.AreEqual(expected, actualResults);
    }

    [TestMethod]
    public void Three_Digits_Yield_ThirtyNine_Representations()
    {
        var sut = "222";

        var actualResults = LetterCombinations.FromPhoneNumber(sut);

        var possibleCombinations = Math.Pow(3,3); //27

        Assert.AreEqual(possibleCombinations, actualResults.Count);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...