私は最初のプログラミングインタビューを試みましたが、質問の1つは、7桁の電話番号を与え、各番号が表すことができるすべての文字の組み合わせを印刷できるプログラムを書くことでした。
質問の2番目の部分は、これが12桁の国際番号だったらどうだったのでしょうか?それはあなたのデザインにどのように影響しますか。
私はインタビューで書いたコードを持っていませんが、彼はそれに満足していない印象を受けました。
これを行う最良の方法は何ですか?
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
はこれまでの結果のリストです。最初は、空の文字列という1つのアイテムが入力されます。次に、入力文字列の各文字について、上部で定義された辞書から一致する文字のリストを検索します。次に、リストret
を既存のプレフィックスと使用可能な文字のすべての組み合わせで置き換えます。
電話番号の文字の組み合わせ と呼ばれる質問に似ていますが、これが私の解決策です。
結果がメモリ制限を超えない限り、任意の桁数で機能します。
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桁の国際番号がデザインにどのように影響するかわかりません。
編集:国際番号も処理されます
In 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 ^桁の数字の組み合わせがあるため、より問題が大きくなります。
それを行う1つの方法は、数字の一致の可能性を数字の数字として(4を基に)調べ、カウンターに近いものを実装することです(通常、数字にマッピングできるのは4文字未満であるため、いくつかのインスタンスを飛び越します) )。
より明白な解決策は、ネストされたループまたは再帰であり、どちらもエレガントではありませんが、私の意見では有効です。
注意が必要なもう1つのことは、多くの組み合わせについて話しているため、スケーラビリティの問題を回避することです(たとえば、メモリに可能性を保持するなど)。
追伸質問の別の興味深い拡張は、ローカリゼーションです。
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
...
数字キーボードでは、テキストと数字は同じキーに配置されます。たとえば、「A」で始まるものを書きたい場合、キー2を1回入力する必要がある場合、2には「ABC」が含まれます。 「B」を入力する場合は、「C」を入力するためにキー2を2回押します。以下はそのようなキーパッドの写真です。
キーパッドhttp://d2o58evtke57tz.cloudfront.net/wp-content/uploads/phoneKeyboard.png
図に示すようにキーパッドとn桁の数字を与え、これらの数字を押すことで可能なすべての単語をリストします。
たとえば、入力番号が234の場合、形成可能な単語は(アルファベット順):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
最初にいくつかの計算をしましょう。各桁がn文字を表す7桁の単語はいくつありますか?最初の数字には最大4つの選択肢があり、最初の文字の各選択肢には2番目の数字に最大4つの選択肢があります。したがって、O(4 ^ n)になる簡単な数学です。キー0と1には対応するアルファベットがなく、多くの文字には3文字があるため、4 ^ nは最小単語数ではなく単語数の上限になります。
それではいくつか例を見てみましょう。
234を超える数については、パターンが表示されますか?はい、最後の文字は常にG、HまたはIのいずれかであり、値をIからGにリセットすると、その左側の数字が変更されることに注意してください。同様に、最後から2番目のアルファベットが値をリセットすると、最後から3番目のアルファベットが変更されます。最初の文字は、すべての単語を生成したときに一度だけリセットされます。これは、もう一方の端からも見ることができます。つまり、位置iの文字が変更されるたびに、位置i + 1の文字は可能なすべての文字を通過し、最後に到達するまで波及効果を作成します。 0と1には文字が関連付けられていないため。これらの数字の反復がないため、中断する必要があります。
再帰を使用して簡単に実装できるため、2番目のアプローチを取りましょう。最後まで行き、一つずつ戻ってきます。再帰に最適な状態。基本ケースを検索しましょう。最後の文字に達すると、最後の桁のすべての可能な文字を含むWordを印刷して戻ります。単純な基本ケース:最後の文字に到達すると、最後の桁のすべての可能な文字を含むWordを印刷して戻ります。シンプルなベースケース。
以下は、n桁の入力番号に対応する可能性のあるすべてのWordを印刷する再帰的アプローチのC実装です。入力番号は、コードを簡素化するために配列として表されることに注意してください。
#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は入力数の桁数です。
参照:
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)
#include <sstream>
#include <map>
#include <vector>
map< int, string> keyMap;
void MakeCombinations( string first, string joinThis , vector<string>& eachResult )
{
if( !first.size() )
return;
int length = joinThis.length();
vector<string> result;
while( length )
{
string each;
char firstCharacter = first.at(0);
each = firstCharacter;
each += joinThis[length -1];
length--;
result.Push_back(each);
}
first = first.substr(1);
vector<string>::iterator begin = result.begin();
vector<string>::iterator end = result.end();
while( begin != end)
{
eachResult.Push_back( *begin);
begin++;
}
return MakeCombinations( first, joinThis, eachResult);
}
void ProduceCombinations( int inNumber, vector<string>& result)
{
vector<string> inputUnits;
int number = inNumber;
while( number )
{
int lastdigit ;
lastdigit = number % 10;
number = number/10;
inputUnits.Push_back( keyMap[lastdigit]);
}
if( inputUnits.size() == 2)
{
MakeCombinations(inputUnits[0], inputUnits[1], result);
}
else if ( inputUnits.size() > 2 )
{
MakeCombinations( inputUnits[0] , inputUnits[1], result);
vector<string>::iterator begin = inputUnits.begin();
vector<string>::iterator end = inputUnits.end();
begin += 2;
while( begin != end )
{
vector<string> intermediate = result;
vector<string>::iterator ibegin = intermediate.begin();
vector<string>::iterator iend = intermediate.end();
while( ibegin != iend)
{
MakeCombinations( *ibegin , *begin, result);
//resultbegin =
ibegin++;
}
begin++;
}
}
else
{
}
return;
}
int _tmain(int argc, _TCHAR* argv[])
{
keyMap[1] = "";
keyMap[2] = "abc";
keyMap[3] = "def";
keyMap[4] = "ghi";
keyMap[5] = "jkl";
keyMap[6] = "mno";
keyMap[7] = "pqrs";
keyMap[8] = "tuv";
keyMap[9] = "wxyz";
keyMap[0] = "";
string inputStr;
getline(cin, inputStr);
int number = 0;
int length = inputStr.length();
int tens = 1;
while( length )
{
number += tens*(inputStr[length -1] - '0');
length--;
tens *= 10;
}
vector<string> r;
ProduceCombinations(number, r);
cout << "[" ;
vector<string>::iterator begin = r.begin();
vector<string>::iterator end = r.end();
while ( begin != end)
{
cout << *begin << "," ;
begin++;
}
cout << "]" ;
return 0;
}
これは this answerの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);
}
}
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;
}
}
}
}
A Pythonソリューションは非常に経済的であり、ジェネレーターを使用するため、メモリの使用に関して効率的です。
import itertools
keys = dict(enumerate('::ABC:DEF:GHI:JKL:MNO:PQRS:TUV:WXYZ'.split(':')))
def words(number):
digits = map(int, str(number))
for ls in itertools.product(*map(keys.get, digits)):
yield ''.join(ls)
for w in words(258):
print w
明らかにitertools.product
は、ほとんどの問題を解決しています。しかし、自分でそれを書くことは難しくありません。 goのソリューションは、配列result
を再使用してすべてのソリューションを生成し、クロージャーf
を再使用して生成された単語をキャプチャすることに注意してください。組み合わせて、これらはproduct
内でO(log n)メモリ使用を与えます。
package main
import (
"bytes"
"fmt"
"strconv"
)
func product(choices [][]byte, result []byte, i int, f func([]byte)) {
if i == len(result) {
f(result)
return
}
for _, c := range choices[i] {
result[i] = c
product(choices, result, i+1, f)
}
}
var keys = bytes.Split([]byte("::ABC:DEF:GHI:JKL:MNO:PQRS:TUV:WXYZ"), []byte(":"))
func words(num int, f func([]byte)) {
ch := [][]byte{}
for _, b := range strconv.Itoa(num) {
ch = append(ch, keys[b-'0'])
}
product(ch, make([]byte, len(ch)), 0, f)
}
func main() {
words(256, func(b []byte) { fmt.Println(string(b)) })
}
リストLを使用します。ここで、L [i] =数字iが表すことができる記号。
L [1] = @、。、! (たとえば)L [2] = a、b、c
等。
次に、このような何かを行うことができます(擬似C):
void f(int k, int st[])
{
if ( k > numberOfDigits )
{
print contents of st[];
return;
}
for each character c in L[Digit At Position k]
{
st[k] = c;
f(k + 1, st);
}
}
各リストに3文字が含まれていると仮定すると、7桁で3 ^ 7、12で3 ^ 12の可能性がありますが、それほど多くはありません。すべての組み合わせが必要な場合、これ以上の方法はありません。再帰などを避けることができますが、何であれ、これよりもはるかに速く何かを取得するつもりはありません。
ここでソース(Scala)を見つけます および ここで動作するアプレット
0と1は文字と一致しないため、数字で自然なブレークポイントを作成します。しかし、それらはすべての数で発生するわけではありません(先頭の0を除く)。 +49567892345のような9桁から始まる長い数字は、OutOfMemoryErrorsにつながる可能性があります。したがって、次のようなグループに番号を分割する方が良いでしょう
短い部分から意味がわかるかどうかを確認してください。私はそのようなプログラムを書き、友人からいくつかの数字をテストしましたが、短い単語の組み合わせを見つけることはめったにありませんでした。
したがって、私の決定は、検索のみをサポートすること、可能な組み合わせを表示すること、完全な自動化を行わないこと、多分複数回、手動で数を分割することを奨励することでした.
それで、+-RAD JUNG(+ -bycicle boy)を見つけました。
スペルミス、略語、外国語、数字としての数字、単語内の数字、および名前を受け入れた場合、解決策を見つけるチャンスは、いじくり回すよりもはるかに優れています。
246848 => 2hot4u (too hot for you)
466368 => goodn8 (good night)
1325 => 1FCK (Football club)
53517 => JDK17 (Java Developer Kit)
人間が観察する可能性のあるものです-アルゴリズムにそのようなものを見つけさせるのはかなり難しいです。
public class Permutation {
//display all combination attached to a 3 digit number
public static void main(String ar[]){
char data[][]= new char[][]{{'a','k','u'},
{'b','l','v'},
{'c','m','w'},
{'d','n','x'},
{'e','o','y'},
{'f','p','z'},
{'g','q','0'},
{'h','r','0'},
{'i','s','0'},
{'j','t','0'}};
int num1, num2, num3=0;
char tempdata[][]= new char[3][3];
StringBuilder number = new StringBuilder("324"); // a 3 digit number
//copy data to a tempdata array-------------------
num1= Integer.parseInt(number.substring(0,1));
tempdata[0] = data[num1];
num2= Integer.parseInt(number.substring(1,2));
tempdata[1] = data[num2];
num3= Integer.parseInt(number.substring(2,3));
tempdata[2] = data[num3];
//display all combinations--------------------
char temp2[][]=tempdata;
char tempd, tempd2;
int i,i2, i3=0;
for(i=0;i<3;i++){
tempd = temp2[0][i];
for (i2=0;i2<3;i2++){
tempd2 = temp2[1][i2];
for(i3=0;i3<3;i3++){
System.out.print(tempd);
System.out.print(tempd2);
System.out.print(temp2[2][i3]);
System.out.println();
}//for i3
}//for i2
}
}
}//end of class
Scalaソリューション:
_def mnemonics(phoneNum: String, dict: IndexedSeq[String]): Iterable[String] = {
def mnemonics(d: Int, prefix: String): Seq[String] = {
if (d >= phoneNum.length) {
Seq(prefix)
} else {
for {
ch <- dict(phoneNum.charAt(d).asDigit)
num <- mnemonics(d + 1, s"$prefix$ch")
} yield num
}
}
mnemonics(0, "")
}
_
各桁が最大4文字にマッピングされると仮定すると、再帰呼び出しの数T
は、_4^n
_の順序である不等式T(n) <= 4T(n-1)
を満たします。
このアプローチはRを使用し、最初に辞書を対応する数字表現に変換し、次にこれをルックアップとして使用することに基づいています。
私のマシンでは変換に1秒しかかからず(約100,000語のUnixネイティブ辞書からの変換)、最大100の異なる数字入力の典型的なルックアップには合計.1秒かかります。
library(data.table)
#example dictionary
dict.orig = tolower(readLines("/usr/share/dict/american-english"))
#split each Word into its constituent letters
#words shorter than the longest padded with "" for simpler retrieval
dictDT = setDT(tstrsplit(dict.orig, split = "", fill = ""))
#lookup table for conversion
#NB: the following are found in the dictionary and would need
# to be handled separately -- ignoring here
# (accents should just be appended to
# matches for unaccented version):
# c("", "'", "á", "â", "å", "ä",
# "ç", "é", "è", "ê", "í", "ñ",
# "ó", "ô", "ö", "û", "ü")
lookup = data.table(num = c(rep('2', 3), rep('3', 3), rep('4', 3),
rep('5', 3), rep('6', 3), rep('7', 4),
rep('8', 3), rep('9', 4)),
let = letters)
#using the lookup table, convert to numeric
for (col in names(dictDT)) {
dictDT[lookup, (col) := i.num, on = setNames("let", col)]
}
#back to character vector
dict.num = do.call(paste0, dictDT)
#sort both for faster vector search
idx = order(dict.num)
dict.num = dict.num[idx]
dict.orig = dict.orig[idx]
possibilities = function(input) dict.orig[dict.num == input]
#sample output:
possibilities('269')
# [1] "amy" "bmw" "cox" "coy" "any" "bow" "box" "boy" "cow" "cox" "coy"
possibilities('22737')
# [1] "acres" "bards" "barer" "bares" "barfs" "baser" "bases" "caper"
# [9] "capes" "cards" "cares" "cases"
私はRubyでそれを試しましたが、別の方法を思い付きました。おそらく現時点では時間と空間のO(?)のように効率的ではありませんが、Rubyの組み込みのArray.productメソッドを使用しているので好きです。どう思いますか?
編集:Python上記で非常に似た解決策を見つけましたが、答えを追加したときにそれを見ていませんでした
def phone_to_abc(phone)
phone_abc = [
'0', '1', 'abc', 'def', 'ghi',
'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'
]
phone_map = phone.chars.map { |x| phone_abc[x.to_i].chars }
result = phone_map[0]
for i in 1..phone_map.size-1
result = result.product(phone_map[i])
end
result.each { |x|
puts "#{x.join}"
}
end
phone_to_abc('86352')
これに対する最新の回答を書き直しました( 上記参照 )、CからJavaへ。上記のコードでは555-5055などの数値がまったく機能しなかったため、0と1(0と1)のサポートも含めました。
ここにあります。一部のコメントは保持されます。
public static void printPhoneWords(int[] number) {
char[] output = new char[number.length];
printWordsUtil(number,0,output);
}
static String[] phoneKeys= new String[]{"0", "1", "ABC", "DEF", "GHI", "JKL",
"MNO", "PQRS", "TUV", "WXYZ"};
private static void printWordsUtil(int[] number, int curDigIndex, char[] output) {
// Base case, if current output Word is done
if (curDigIndex == output.length) {
System.out.print(String.valueOf(output) + " ");
return;
}
// Try all 3-4 possible characters for the current digit in number[]
// and recurse for the remaining digits
char curPhoneKey[] = phoneKeys[number[curDigIndex]].toCharArray();
for (int i = 0; i< curPhoneKey.length ; i++) {
output[curDigIndex] = curPhoneKey[i];
printWordsUtil(number, curDigIndex+1, output);
if (number[curDigIndex] <= 1) // for 0 or 1
return;
}
}
public static void main(String[] args) {
int number[] = {2, 3, 4};
printPhoneWords(number);
System.out.println();
}
static final String[] keypad = {"", "", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"};
String[] printAlphabet(int num){
if (num >= 0 && num < 10){
String[] retStr;
if (num == 0 || num ==1){
retStr = new String[]{""};
} else {
retStr = new String[keypad[num].length()];
for (int i = 0 ; i < keypad[num].length(); i++){
retStr[i] = String.valueOf(keypad[num].charAt(i));
}
}
return retStr;
}
String[] nxtStr = printAlphabet(num/10);
int digit = num % 10;
String[] curStr = null;
if(digit == 0 || digit == 1){
curStr = new String[]{""};
} else {
curStr = new String[keypad[digit].length()];
for (int i = 0; i < keypad[digit].length(); i++){
curStr[i] = String.valueOf(keypad[digit].charAt(i));
}
}
String[] result = new String[curStr.length * nxtStr.length];
int k=0;
for (String cStr : curStr){
for (String nStr : nxtStr){
result[k++] = nStr + cStr;
}
}
return result;
}
private List<string> strs = new List<string>();
char[] numbersArray;
private int End = 0;
private int numberOfStrings;
private void PrintLetters(string numbers)
{
this.End = numbers.Length;
this.numbersArray = numbers.ToCharArray();
this.PrintAllCombinations(this.GetCharacters(this.numbersArray[0]), string.Empty, 0);
}
private void PrintAllCombinations(char[] letters, string output, int depth)
{
depth++;
for (int i = 0; i < letters.Length; i++)
{
if (depth != this.End)
{
output += letters[i];
this.PrintAllCombinations(this.GetCharacters(Convert.ToChar(this.numbersArray[depth])), output, depth);
output = output.Substring(0, output.Length - 1);
}
else
{
Console.WriteLine(output + letters[i] + (++numberOfStrings));
}
}
}
private char[] GetCharacters(char x)
{
char[] arr;
switch (x)
{
case '0': arr = new char[1] { '.' };
return arr;
case '1': arr = new char[1] { '.' };
return arr;
case '2': arr = new char[3] { 'a', 'b', 'c' };
return arr;
case '3': arr = new char[3] { 'd', 'e', 'f' };
return arr;
case '4': arr = new char[3] { 'g', 'h', 'i' };
return arr;
case '5': arr = new char[3] { 'j', 'k', 'l' };
return arr;
case '6': arr = new char[3] { 'm', 'n', 'o' };
return arr;
case '7': arr = new char[4] { 'p', 'q', 'r', 's' };
return arr;
case '8': arr = new char[3] { 't', 'u', 'v' };
return arr;
case '9': arr = new char[4] { 'w', 'x', 'y', 'z' };
return arr;
default: return null;
}
}
ここに痛みCの1つがあります。これはリクリーではなく、効果的ではありません(実際、まったくそうではないと思います)。しかし、それにはいくつかの興味深い側面があります。
これの良い点は、文字列のサイズに実際の制限がないことです(文字制限なし)。これにより、待機するだけで必要に応じてより長い文字列を生成できます。
#include <stdlib.h>
#include <stdio.h>
#define CHARACTER_RANGE 95 // The range of valid password characters
#define CHARACTER_OFFSET 32 // The offset of the first valid character
void main(int argc, char *argv[], char *env[])
{
int i;
long int string;
long int worker;
char *guess; // Current Generation
guess = (char*)malloc(1); // Allocate it so free doesn't fail
int cur;
for ( string = 0; ; string++ )
{
worker = string;
free(guess);
guess = (char*)malloc((string/CHARACTER_RANGE+1)*sizeof(char)); // Alocate for the number of characters we will need
for ( i = 0; worker > 0 && i < string/CHARACTER_RANGE + 1; i++ ) // Work out the string
{
cur = worker % CHARACTER_RANGE; // Take off the last digit
worker = (worker - cur) / CHARACTER_RANGE; // Advance the digits
cur += CHARACTER_OFFSET;
guess[i] = cur;
}
guess[i+1] = '\0'; // NULL terminate our string
printf("%s\t%d\n", guess, string);
}
}
/**
* Simple Java implementation without any input/error checking
* (expects all digits as input)
**/
public class PhoneSpeller
{
private static final char[][] _letters = new char[][]{
{'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 main(String[] args)
{
if (args.length != 1)
{
System.out.println("Please run again with your phone number (no dashes)");
System.exit(0);
}
recursive_phoneSpell(args[0], 0, new ArrayList<String>());
}
private static void recursive_phoneSpell(String arg, int index, List<String> results)
{
if (index == arg.length())
{
printResults(results);
return;
}
int num = Integer.parseInt(arg.charAt(index)+"");
if (index==0)
{
for (int j = 0; j<_letters[num].length; j++)
{
results.add(_letters[num][j]+"");
}
recursive_phoneSpell(arg, index+1, results);
}
else
{
List<String> combos = new ArrayList<String>();
for (int j = 0; j<_letters[num].length; j++)
{
for (String result : results)
{
combos.add(result+_letters[num][j]);
}
}
recursive_phoneSpell(arg, index+1, combos);
}
}
private static void printResults(List<String> results)
{
for (String result : results)
{
System.out.println(result);
}
}
}
これは、C++ 11の再帰アルゴリズムです。
#include <iostream>
#include <array>
#include <list>
std::array<std::string, 10> pm = {
"0", "1", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"
};
void generate_mnemonic(const std::string& numbers, size_t i, std::string& m,
std::list<std::string>& mnemonics)
{
// Base case
if (numbers.size() == i) {
mnemonics.Push_back(m);
return;
}
for (char c : pm[numbers[i] - '0']) {
m[i] = c;
generate_mnemonic(numbers, i + 1, m, mnemonics);
}
}
std::list<std::string> phone_number_mnemonics(const std::string& numbers)
{
std::list<std::string> mnemonics;
std::string m(numbers.size(), 0);
generate_mnemonic(numbers, 0, m, mnemonics);
return mnemonics;
}
int main() {
std::list<std::string> result = phone_number_mnemonics("2276696");
for (const std::string& s : result) {
std::cout << s << std::endl;
}
return 0;
}
Objective-Cの1つのオプション:
- (NSArray *)lettersForNumber:(NSNumber *)number {
switch ([number intValue]) {
case 2:
return @[@"A",@"B",@"C"];
case 3:
return @[@"D",@"E",@"F"];
case 4:
return @[@"G",@"H",@"I"];
case 5:
return @[@"J",@"K",@"L"];
case 6:
return @[@"M",@"N",@"O"];
case 7:
return @[@"P",@"Q",@"R",@"S"];
case 8:
return @[@"T",@"U",@"V"];
case 9:
return @[@"W",@"X",@"Y",@"Z"];
default:
return nil;
}
}
- (NSArray *)letterCombinationsForNumbers:(NSArray *)numbers {
NSMutableArray *combinations = [[NSMutableArray alloc] initWithObjects:@"", nil];
for (NSNumber *number in numbers) {
NSArray *lettersNumber = [self lettersForNumber:number];
//Ignore numbers that don't have associated letters
if (lettersNumber.count == 0) {
continue;
}
NSMutableArray *currentCombinations = [combinations mutableCopy];
combinations = [[NSMutableArray alloc] init];
for (NSString *letter in lettersNumber) {
for (NSString *letterInResult in currentCombinations) {
NSString *newString = [NSString stringWithFormat:@"%@%@", letterInResult, letter];
[combinations addObject:newString];
}
}
}
return combinations;
}
</ code>
再帰の回数を減らすのに役立つキャッシュを実装しました。このキャッシュは、以前の組み合わせですでに行った文字のスキャンを回避します。 1つの例では、120kループを使用していましたが、キャッシュの実装後は40kに削減されました。
private List<String> generateWords(String phoneNumber) {
List<String> words = new LinkedList<String>();
List<String> wordsCache = new LinkedList<String>();
this.generatePossibleWords("", phoneNumber, words, wordsCache);
return words;
}
private void generatePossibleWords(String prefix, String remainder,
List<String> words, List<String> wordsCache) {
int index = Integer.parseInt(remainder.substring(0, 1));
for (int i = 0; i < phoneKeyMapper.get(index).size(); i++) {
String mappedChar = phoneKeyMapper.get(index).get(i);
if (prefix.equals("") && !wordsCache.isEmpty()) {
for (String Word : wordsCache) {
words.add(mappedChar + Word);
}
} else {
if (remainder.length() == 1) {
String stringToBeAdded = prefix + mappedChar;
words.add(stringToBeAdded);
wordsCache.add(stringToBeAdded.substring(1));
LOGGER.finest("adding stringToBeAdded = " + stringToBeAdded.substring(1));
} else {
generatePossibleWords(prefix + mappedChar,
remainder.substring(1), words, wordsCache);
}
}
}
}
private void createPhoneNumberMapper() {
if (phoneKeyMapper == null) {
phoneKeyMapper = new ArrayList<>();
phoneKeyMapper.add(0, new ArrayList<String>());
phoneKeyMapper.add(1, new ArrayList<String>());
phoneKeyMapper.add(2, new ArrayList<String>());
phoneKeyMapper.get(2).add("A");
phoneKeyMapper.get(2).add("B");
phoneKeyMapper.get(2).add("C");
phoneKeyMapper.add(3, new ArrayList<String>());
phoneKeyMapper.get(3).add("D");
phoneKeyMapper.get(3).add("E");
phoneKeyMapper.get(3).add("F");
phoneKeyMapper.add(4, new ArrayList<String>());
phoneKeyMapper.get(4).add("G");
phoneKeyMapper.get(4).add("H");
phoneKeyMapper.get(4).add("I");
phoneKeyMapper.add(5, new ArrayList<String>());
phoneKeyMapper.get(5).add("J");
phoneKeyMapper.get(5).add("K");
phoneKeyMapper.get(5).add("L");
phoneKeyMapper.add(6, new ArrayList<String>());
phoneKeyMapper.get(6).add("M");
phoneKeyMapper.get(6).add("N");
phoneKeyMapper.get(6).add("O");
phoneKeyMapper.add(7, new ArrayList<String>());
phoneKeyMapper.get(7).add("P");
phoneKeyMapper.get(7).add("Q");
phoneKeyMapper.get(7).add("R");
phoneKeyMapper.get(7).add("S");
phoneKeyMapper.add(8, new ArrayList<String>());
phoneKeyMapper.get(8).add("T");
phoneKeyMapper.get(8).add("U");
phoneKeyMapper.get(8).add("V");
phoneKeyMapper.add(9, new ArrayList<String>());
phoneKeyMapper.get(9).add("W");
phoneKeyMapper.get(9).add("X");
phoneKeyMapper.get(9).add("Y");
phoneKeyMapper.get(9).add("Z");
}
}
就職の面接で、電話番号の可能なすべての文字の組み合わせを作成し、印刷することに関するこの質問を受け取りました。インタビュー中、私はインタビュアーから再帰と、ループを使用してそれを行うことができない方法についてささやきを受け取りました。私が他のプログラマーから入力を受け取るのは非常に不自然で、私は自分の授業料の代わりに彼のアドバイスを信頼し、ずさんな再帰混乱を書き始めました。うまくいきませんでした。入力を受け取る前に、これまでこの問題を受け取ったことはなかったので、私の脳は基礎となる複製可能な数式を計算していました。私の心が1つの答えを考えながら別の文章を書き始めて短いクロックと競い合い始めたとき、「3倍するだけでなく、4倍になります」とbreathきます。悲しいニュースを知らせるために電話を受け取ったのは、週の終わりまでではありませんでした。その夜遅くになって、本当に再帰の問題かどうかを確認することにしました。私にとってはそうではありません。
以下の私のソリューションはPHPでコーディングされており、非再帰的であり、任意の長さの入力で動作し、変数が何を意味するのかを説明するためのコメントも含めています。この質問に対する私の公式かつ不変の最終回答。これが将来誰かに役立つことを願っています。これを他の言語に自由に翻訳してください。
私の方法では、組み合わせの総数を計算し、すべてのバイトを保持するのに十分な大きさのバッファーを作成します。私のバッファの長さは、動作中の再帰アルゴリズムから受け取ると同じ長さです。各数値を表す文字のグループを含む配列を作成します。あなたのコンボ合計は常に現在のグループの文字数で割り切れるので、私の方法は主に機能します。頭の中にこのアルゴリズムの出力の垂直リストを想像できるか、または組み合わせの水平に書かれたリストとは対照的にリストを垂直に見ることができれば、私のアルゴリズムは意味をなし始めます。このアルゴリズムにより、各バイトを左から右ではなく左から上から下に垂直にループし、再帰を必要とせずに各バイトを個別に設定できます。バッファをバイト配列のように使用して、時間とエネルギーを節約します。
非再帰的、反復PHP
<?php
// Display all possible combinations of letters for each number.
$input = '23456789';
// ====================
if(!isset($input[0]))
die('Nothing to see here!');
$phone_letters = array(
2 => array('a', 'b', 'c'),
3 => array('d', 'e', 'f'),
4 => array('g', 'h', 'i'),
5 => array('j', 'k', 'l'),
6 => array('m', 'n', 'o'),
7 => array('p', 'q', 'r', 's'),
8 => array('t', 'u', 'v'),
9 => array('w', 'x', 'y', 'z')
);
$groups = array();
$combos_total = 1;
$l = strlen($input);
for($i = 0; $i < $l; ++$i) {
$groups[] = $phone_letters[$input[$i]];
$combos_total *= count($phone_letters[$input[$i]]);
}
$count = $combos_total / count($groups[0]);
$combos = array_fill(0, $combos_total, str_repeat(chr(0), strlen($input)));
for(
$group = 0, // Index for the current group of letters.
$groups_count = count($groups), // Total number of letter groups.
$letters = count($groups[0]), // Total number of letters in the current group.
$width = $combos_total, // Number of bytes to repeat the current letter.
$repeat = 1; // Total number of times the group will repeat.
;
) {
for($byte = 0, $width /= $letters, $r = 0; $r < $repeat; ++$r)
for($l = 0; $l < $letters; ++$l)
for($w = 0; $w < $width; ++$w)
$combos[$byte++][$group] = $groups[$group][$l];
if(++$group < $groups_count) {
$repeat *= $letters;
$letters = count($groups[$group]);
}
else
break;
}
// ====================
if(is_array($combos)) {
print_r($combos);
echo 'Total combos:', count($combos), "\n";
}
else
echo 'No combos.', "\n";
echo 'Expected combos: ', $combos_total, "\n";
非再帰的、非反復的、バッファなし、スレッドフレンドリ、数学のみ+非再帰的、反復PHP MULTI-BASE BIG ENDIANを使用するオブジェクト
先日、新しい家で働いていたときに、最初の関数の数学をベースの知識と組み合わせて使用して、入力のすべての可能な文字の組み合わせを多次元数として扱うことができることに気付きました。
私のオブジェクトは、好みの組み合わせをデータベースに保存し、必要に応じて文字と数字を変換し、好みの組み合わせまたはバイトを使用して組み合わせスペースのどこからでも組み合わせを選択し、すべてマルチスレッドで非常にうまく機能するために必要な機能を提供します。
そうは言っても、私のオブジェクトを使用して、ベース、バッファーなし、反復なし、そして最も重要なこととして再帰を使用する2番目の答えを提供できます。
<?php
class phone {
public static $letters = array(
2 => array('a', 'b', 'c'),
3 => array('d', 'e', 'f'),
4 => array('g', 'h', 'i'),
5 => array('j', 'k', 'l'),
6 => array('m', 'n', 'o'),
7 => array('p', 'q', 'r', 's'),
8 => array('t', 'u', 'v'),
9 => array('w', 'x', 'y', 'z')
);
// Convert a letter into its respective number.
public static function letter_number($letter) {
if(!isset($letter[0]) || isset($letter[1]))
return false;
for($i = 2; $i < 10; ++$i)
if(in_array($letter, phone::$letters[$i], true))
return $i;
return false;
}
// Convert letters into their respective numbers.
public static function letters_numbers($letters) {
if(!isset($letters[0]))
return false;
$length = strlen($letters);
$numbers = str_repeat(chr(0), $length);
for($i = 0; $i < $length; ++$i) {
for($j = 2; $j < 10; ++$j) {
if(in_array($letters[$i], phone::$letters[$j], true)) {
$numbers[$i] = $j;
break;
}
}
}
return $numbers;
}
// Calculate the maximum number of combinations that could occur within a particular input size.
public static function combination_size($groups) {
return $groups <= 0 ? false : pow(4, $groups);
}
// Calculate the minimum bytes reqired to store a group using the current input.
public static function combination_bytes($groups) {
if($groups <= 0)
return false;
$size = $groups * 4;
$bytes = 0;
while($groups !== 0) {
$groups >> 8;
++$bytes;
}
return $bytes;
}
public $input = '';
public $input_len = 0;
public $combinations_total = 0;
public $combinations_length = 0;
private $iterations = array();
private $branches = array();
function __construct($number) {
if(!isset($number[0]))
return false;
$this->input = $number;
$input_len = strlen($number);
$combinations_total = 1;
for($i = 0; $i < $input_len; ++$i) {
$combinations_total *= count(phone::$letters[$number[$i]]);
}
$this->input_len = $input_len;
$this->combinations_total = $combinations_total;
$this->combinations_length = $combinations_total * $input_len;
for($i = 0; $i < $input_len; ++$i) {
$this->branches[] = $this->combination_branches($i);
$this->iterations[] = $this->combination_iteration($i);
}
}
// Calculate a particular combination in the combination space and return the details of that combination.
public function combination($combination) {
$position = $combination * $this->input_len;
if($position < 0 || $position >= $this->combinations_length)
return false;
$group = $position % $this->input_len;
$first = $position - $group;
$last = $first + $this->input_len - 1;
$combination = floor(($last + 1) / $this->input_len) - 1;
$bytes = str_repeat(chr(0), $this->input_len);
for($i = 0; $i < $this->input_len; ++$i)
$bytes[$i] = phone::$letters[$this->input[$i]][($combination / $this->branches[$i]) % count(phone::$letters[$this->input[$i]])];
return array(
'combination' => $combination,
'branches' => $this->branches[$group],
'iterations' => $this->iterations[$group],
'first' => $first,
'last' => $last,
'bytes' => $bytes
);
}
// Calculate a particular byte in the combination space and return the details of that byte.
public function combination_position($position) {
if($position < 0 || $position >= $this->combinations_length)
return false;
$group = $position % $this->input_len;
$group_count = count(phone::$letters[$this->input[$group]]);
$first = $position - $group;
$last = $first + $this->input_len - 1;
$combination = floor(($last + 1) / $this->input_len) - 1;
$index = ($combination / $this->branches[$group]) % $group_count;
$bytes = str_repeat(chr(0), $this->input_len);
for($i = 0; $i < $this->input_len; ++$i)
$bytes[$i] = phone::$letters[$this->input[$i]][($combination / $this->branches[$i]) % count(phone::$letters[$this->input[$i]])];
return array(
'position' => $position,
'combination' => $combination - 1,
'group' => $group,
'group_count' => $group_count,
'group_index' => $index,
'number' => $this->input[$group],
'branches' => $this->branches[$group],
'iterations' => $this->iterations[$group],
'first' => $first,
'last' => $last,
'byte' => phone::$letters[$this->input[$group]][$index],
'bytes' => $bytes
);
}
// Convert letters into a combination number using Multi-Base Big Endian.
public function combination_letters($letters) {
if(!isset($letters[$this->input_len - 1]) || isset($letters[$this->input_len]))
return false;
$combination = 0;
for($byte = 0; $byte < $this->input_len; ++$byte) {
$base = count(phone::$letters[$this->input[$byte]]);
$found = false;
for($value = 0; $value < $base; ++$value) {
if($letters[$byte] === phone::$letters[$this->input[$byte]][$value]) {
$combination += $value * $this->branches[$byte];
$found = true;
break;
}
}
if($found === false)
return false;
}
return $combination;
}
// Calculate the number of branches after a particular group.
public function combination_branches($group) {
if($group >= 0 && ++$group < $this->input_len) {
$branches = 1;
for($i = $group; $i < $this->input_len; ++$i)
$branches *= count(phone::$letters[$this->input[$i]]);
return $branches === 1 ? 1 : $branches;
}
elseif($group === $this->input_len)
return 1;
else
return false;
}
// Calculate the number of iterations before a particular group.
public function combination_iteration($group) {
if($group > 0 && $group < $this->input_len) {
$iterations = 1;
for($i = $group - 1; $i >= 0; --$i)
$iterations *= count(phone::$letters[$this->input[$i]]);
return $iterations;
}
elseif($group === 0)
return 1;
else
return false;
}
// Iterations, Outputs array of all combinations using a buffer.
public function combinations() {
$count = $this->combinations_total / count(phone::$letters[$this->input[0]]);
$combinations = array_fill(0, $this->combinations_total, str_repeat(chr(0), $this->input_len));
for(
$group = 0, // Index for the current group of letters.
$groups_count = $this->input_len, // Total number of letter groups.
$letters = count(phone::$letters[$this->input[0]]), // Total number of letters in the current group.
$width = $this->combinations_total, // Number of bytes to repeat the current letter.
$repeat = 1; // Total number of times the group will repeat.
;
) {
for($byte = 0, $width /= $letters, $r = 0; $r < $repeat; ++$r)
for($l = 0; $l < $letters; ++$l)
for($w = 0; $w < $width; ++$w)
$combinations[$byte++][$group] = phone::$letters[$this->input[$group]][$l];
if(++$group < $groups_count) {
$repeat *= $letters;
$letters = count(phone::$letters[$this->input[$group]]);
}
else
break;
}
return $combinations;
}
}
// ====================
$phone = new phone('23456789');
print_r($phone);
//print_r($phone->combinations());
for($i = 0; $i < $phone->combinations_total; ++$i) {
echo $i, ': ', $phone->combination($i)['bytes'], "\n";
}
ネストされたループを使用したRソリューション:
# Create phone pad
two <- c("A", "B", "C")
three <- c("D", "E", "F")
four <- c("G", "H", "I")
five <- c("J", "K", "L")
six <- c("M", "N", "O", "P")
seven <- c("Q", "R", "S")
eight <- c("T", "U", "V")
nine <- c("W", "X", "Y", "Z")
# Choose three numbers
number_1 <- two
number_2 <- three
number_3 <- six
# create an object to save the combinations to
combinations <- NULL
# Loop through the letters in number_1
for(i in number_1){
# Loop through the letters in number_2
for(j in number_2){
# Loop through the letters in number_3
for(k in number_3){
# Add each of the letters to the combinations object
combinations <- c(combinations, paste0(i, j, k))
}
}
}
# Print all of the possible combinations
combinations
より多くのループとサンプリングを使用する、より奇妙な別のRソリューションを投稿しました 私のブログで 。
Oracle SQL:任意の電話番号で使用でき、ローカライズを簡単にサポートできます。
CREATE TABLE digit_character_map (digit number(1), character varchar2(1));
SELECT replace(permutations,' ','') AS permutations
FROM (SELECT sys_connect_by_path(map.CHARACTER,' ') AS permutations, LEVEL AS lvl
FROM digit_character_map map
START WITH map.digit = substr('12345',1,1)
CONNECT BY digit = substr('12345',LEVEL,1))
WHERE lvl = length('12345');
C#のこのバージョンはかなり効率的であり、非西側の数字(たとえば「۱۲۳۴۵۶۷」など)で機能します。
static void Main(string[] args)
{
string phoneNumber = null;
if (1 <= args.Length)
phoneNumber = args[0];
if (string.IsNullOrEmpty(phoneNumber))
{
Console.WriteLine("No phone number supplied.");
return;
}
else
{
Console.WriteLine("Alphabetic phone numbers for \"{0}\":", phoneNumber);
foreach (string phoneNumberText in GetPhoneNumberCombos(phoneNumber))
Console.Write("{0}\t", phoneNumberText);
}
}
public static IEnumerable<string> GetPhoneNumberCombos(string phoneNumber)
{
phoneNumber = RemoveNondigits(phoneNumber);
if (string.IsNullOrEmpty(phoneNumber))
return new List<string>();
char[] combo = new char[phoneNumber.Length];
return GetRemainingPhoneNumberCombos(phoneNumber, combo, 0);
}
private static string RemoveNondigits(string phoneNumber)
{
if (phoneNumber == null)
return null;
StringBuilder sb = new StringBuilder();
foreach (char nextChar in phoneNumber)
if (char.IsDigit(nextChar))
sb.Append(nextChar);
return sb.ToString();
}
private static IEnumerable<string> GetRemainingPhoneNumberCombos(string phoneNumber, char[] combo, int nextDigitIndex)
{
if (combo.Length - 1 == nextDigitIndex)
{
foreach (char nextLetter in phoneNumberAlphaMapping[(int)char.GetNumericValue(phoneNumber[nextDigitIndex])])
{
combo[nextDigitIndex] = nextLetter;
yield return new string(combo);
}
}
else
{
foreach (char nextLetter in phoneNumberAlphaMapping[(int)char.GetNumericValue(phoneNumber[nextDigitIndex])])
{
combo[nextDigitIndex] = nextLetter;
foreach (string result in GetRemainingPhoneNumberCombos(phoneNumber, combo, nextDigitIndex + 1))
yield return result;
}
}
}
private static char[][] phoneNumberAlphaMapping = new char[][]
{
new char[] { '0' },
new char[] { '1' },
new char[] { 'a', 'b', 'c' },
new char[] { 'd', 'e', 'f' },
new char[] { 'g', 'h', 'i' },
new char[] { 'j', 'k', 'l' },
new char[] { 'm', 'n', 'o' },
new char[] { 'p', 'q', 'r', 's' },
new char[] { 't', 'u', 'v' },
new char[] { 'w', 'x', 'y', 'z' }
};
私はかなり初心者なので、間違っているところはどこでも修正してください。
まず、スペースと時間の複雑さを調べます。これは階乗であるため本当に悪いので、factorial(7)= 5040の場合、どの再帰アルゴリズムでも実行できます。ただし、factorial(12)〜= 4 * 10 ^ 8の場合、再帰的ソリューションでスタックオーバーフローが発生する可能性があります。
そのため、再帰アルゴリズムを試みません。ループソリューションは、「Next Permutation」を使用して非常に簡単です。
したがって、{0、1、2、3、4、5}を作成して配列し、すべての順列を生成し、印刷中にそれらをそれぞれの文字に置き換えます。 0 = A、5 = F
次のPermアルゴリズムは次のように機能します。例:1,3,5,4の場合、次の順列は1,4,3,5です
次のパーマを見つけるための手順。
右から左に、最初に減少する番号を見つけます。例3
左から順に、3より大きい最小数を見つけます。 4
サブセットを逆にしてこれらの数値を交換します。 1,4,5,3逆サブセット1,4,3,5
次の順列(または回転)を使用して、特定の順列のサブセットを生成します。特定の電話番号から始まる1000個の順列を表示するとします。これにより、すべての数字をメモリに保存する必要がなくなります。数値を4バイト整数として保存する場合、10 ^ 9バイト= 1 GB!.
ここに同じの作業コードがあります..各ステップでの再帰は、一連の同じ数字の発生時に各組み合わせの可能性をチェックします....複雑さの観点からこれより良い解決策があるかどうかはわかりません.....
問題があれば教えてください。
import Java.util.ArrayList;
public class phonenumbers {
/**
* @param args
*/
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 main(String[] args) {
// TODO Auto-generated method stub
String phone = "3333456789";
ArrayList<String> list= generateAllnums(phone,"",0);
}
private static ArrayList<String> generateAllnums(String phone,String sofar,int j) {
// TODO Auto-generated method stub
//System.out.println(phone);
if(phone.isEmpty()){
System.out.println(sofar);
for(int k1=0;k1<sofar.length();k1++){
int m=sofar.toLowerCase().charAt(k1)-48;
if(m==-16)
continue;
int i=k1;
while(true && i<sofar.length()-2){
if(sofar.charAt(i+1)==' ')
break;
else if(sofar.charAt(i+1)==sofar.charAt(k1)){
i++;
}else{
break;
}
}
i=i-k1;
//System.out.print(" " + m +", " + i + " ");
System.out.print(mappings[m][i%3]);
k1=k1+i;
}
System.out.println();
return null;
}
int num= phone.charAt(j);
int k=0;
for(int i=j+1;i<phone.length();i++){
if(phone.charAt(i)==num){
k++;
}
}
if(k!=0){
int p=0;
ArrayList<String> list2= generateAllnums(phone.substring(p+1), sofar+phone.charAt(p)+ " ", 0);
ArrayList<String> list3= generateAllnums(phone.substring(p+1), sofar+phone.charAt(p), 0);
}
else{
ArrayList<String> list1= generateAllnums(phone.substring(1), sofar+phone.charAt(0), 0);
}
return null;
}
}