使用电话键盘生成 10 位数字

Posted

技术标签:

【中文标题】使用电话键盘生成 10 位数字【英文标题】:Generate 10-digit number using a phone keypad 【发布时间】:2011-02-23 00:31:45 【问题描述】:

给定一个电话键盘,如下图:

1 2 3
4 5 6
7 8 9
  0

从 1 开始可以组成多少个不同的 10 位数字?约束是从一位数到下一位数的移动类似于象棋游戏中马的移动。

例如。如果我们是 1 那么下一个数字可以是 6 或 8 如果我们是 6,那么下一个数字可以是 1、7 或 0。

允许重复数字 - 1616161616 是有效数字。

有解决这个问题的多项式时间算法吗?这个问题要求我们只给出 10 位数字的计数,而不一定要列出这些数字。

编辑:我尝试将此建模为一个图形,每个数字都有 2 或 3 个数字作为其邻居。然后我使用 DFS 导航到 10 个节点的深度,然后每次达到 10 的深度时增加数字计数。这显然不是多项式时间。假设每个数字只有 2 个邻居,这将需要至少 2^10 次迭代。

这里的变量是位数。我已经采取了例如。 10 位数字。也可以是 n 位数。

【问题讨论】:

家庭作业?到目前为止,您尝试过什么? @srikfreak:如果这实际上是家庭作业,那么如果你给它打上这个标签,你会得到最好的结果,并表明你做了哪些诚实的努力以及你在哪里卡住了。这里的大多数人都不想为你做作业,即使有人做了你也不会学习。 这是在一次采访中被问到的。我已将其标记为面试问题。 5 永远无法达到,因此对于这个特定问题可以安全地忽略它,而不会对解决方案产生任何影响。 对于那些想检查他们的解决方案的人,正确答案是1424 【参考方案1】:

我实现了蛮力和动态编程模型

import queue


def chess_numbers_bf(start, length):
    if length <= 0:
        return 0
    phone = [[7, 5], [6, 8], [3, 7], [9, 2, 8], [], [6, 9, 0], [1, 5], [0, 2], [3, 1], [5, 3]]
    total = 0
    q = queue.Queue()
    q.put((start, 1))

    while not q.empty():
        front = q.get()
        val = front[0]
        len_ = front[1]
        if len_ < length:
            for elm in phone[val]:
                q.put((elm, len_ + 1))
        else:
            total += 1
    return total


def chess_numbers_dp(start, length):
    if length <= 0:
        return 0

    phone = [[7, 5], [6, 8], [3, 7], [9, 2, 8], [], [6, 9, 0], [1, 5], [0, 2], [3, 1], [5, 3]]
    memory = 

    def __chess_numbers_dp(s, l):
        if (s, l) in memory:
            return memory[(s, l)]
        elif l == length - 1:
            memory[(s, l)] = 1
            return 1
        else:
            total_n_ways = 0
            for number in phone[s]:
                total_n_ways += __chess_numbers_dp(number, l+1)
            memory[(s, l)] = total_n_ways
            return total_n_ways
    return __chess_numbers_dp(start, 0)


# bf
for i in range(0, 10):
    print(i, chess_numbers_bf(3, i))
print('\n')

for i in range(0, 10):
    print(i, chess_numbers_bf(9, i))
print('\n')

# dp
for i in range(0, 10):
    print(i, chess_numbers_dp(3, i))
print('\n')

# dp
for i in range(0, 10):
    print(i, chess_numbers_dp(9, i))
print('\n')

【讨论】:

【参考方案2】:

递归记忆方法:

vector<vector<int>> lupt =  4, 6, 6, 8, 9, 7, 4, 8, 3, 9, 0,
                             ,     1,7,0, 6, 2, 1, 3, 2, 4 ;

int numPhoneNumbersUtil(int startdigit, int& phonenumberlength, int currCount, map< pair<int,int>,int>& memT)

    int noOfCombs = 0;
    vector<int> enddigits;

    auto it = memT.find(make_pair(startdigit,currCount));
    if(it != memT.end())
    
        noOfCombs = it->second;
        return noOfCombs;
    

    if(currCount == phonenumberlength)
    
        return 1;
    

    enddigits = lupt[startdigit];
    for(auto it : enddigits)
    
        noOfCombs += numPhoneNumbersUtil(it, phonenumberlength, currCount + 1, memT);
    

    memT.insert(make_pair(make_pair(startdigit,currCount), noOfCombs));
    return memT[make_pair(startdigit,currCount)];



int numPhoneNumbers(int startdigit, int phonenumberlength)

    map<pair<int,int>,int> memT;
    int currentCount = 1; //the first digit has already been added
    return  numPhoneNumbersUtil(startdigit, phonenumberlength, currentCount, memT);

【讨论】:

【参考方案3】:

我不确定我是否遗漏了什么,但是阅读问题的描述后,我找到了这个解决方案。它的时间复杂度为 O(n),空间复杂度为 O(1)。

我认为数字 1 在拐角处,对吧?在每个角落,您可以移动到一侧(9 和 3 中的 4,或 7 和 1 中的 6)或“垂直”侧之一(3 和 1 中的 8,或 9 和 7 中的 2)。因此,角落增加了两个动作:侧向移动和“垂直”移动。这适用于所有四个角 (1,3,9,7)。

您可以从每一侧移动到两个角(从 6 到 7 和 1,从 4 到 9 和 3),或者您可以到达底部键 (0)。也就是三招。两角一底。

在底部键 (0) 上,您可以移动到两侧(4 和 6)。因此,在每一步中,您检查前一个长度的路径的所有可能结尾(即,有多少在角、边、“垂直”或“底部”零键上结束),然后生成新的结尾根据前面所说的生成规则进行计数。

每个角的末端都增加了一条边和一条垂直线。 每个侧边末端增加 2 个角和一个底部。 每个垂直末端添加 2 个角。 每个底端增加 2 个边。

如果您从“1”键开始,则从一个可能的角解决方案开始,在每一步中,您计算​​上一步的角、边、垂直和底端的数量,然后应用规则生成下一个数。

在纯 javascript 代码中。

function paths(n) 
    //Index to 0
    var corners = 1;
    var verticals = 0;
    var bottom = 0;
    var sides = 0;

    if (n <= 0) 
        //No moves possible for paths without length 
        return 0;
    

    for (var i = 1; i < n; i++) 
        var previousCorners = corners;
        var previousVerticals = verticals;
        var previousBottom = bottom;
        var previousSides = sides;

        sides = 1 * previousCorners + 2 * previousBottom;
        verticals = 1 * previousCorners;
        bottom = 1 * previousSides;
        corners = 2 * previousSides + 2 * previousVerticals;
        //console.log("Moves: %d, Length: %d, Sides: %d, Verticals: %d, Bottom: %d, Corners: %d, Total: %d", i, i + 1, sides, verticals, bottom, corners, sides+verticals+bottom+corners);  
    

    return sides + verticals + bottom + corners;



for (var i = 0; i <= 10; i++) 
    console.log(paths(i));  

【讨论】:

很好的观察和解决方案。我认为这类似于自下而上的动态编程方法,您只维护前一行。【参考方案4】:

方法返回从 1 开始的 10 位数字列表。计数再次为 1424。

  public ArrayList<String> getList(int digit, int length, String base )
    ArrayList<String> list = new ArrayList<String>();
    if(length == 1)
        list.add(base);
        return list;
    
    ArrayList<String> temp;

    for(int i : b[digit])
        String newBase = base +i;
        list.addAll(getList(i, length -1, newBase ));
    
    return list;

【讨论】:

【参考方案5】:
//Both the iterative and recursive with memorize shows count as 1424 for 10 digit numbers starting with 1. 
int[][] b = 4,6,6,8,7,9,4,8,0,3,9,,1,7,0,2,6,1,3,2,4;
public int countIterative(int digit, int length) 
    int[][] matrix = new int[length][10];
    for(int dig =0; dig <=9; dig++)
          matrix[0][dig] = 1;
    
    for(int len = 1; len < length; len++)
        for(int dig =0; dig <=9; dig++)
          int sum = 0;
          for(int i : b[dig])
            sum += matrix[len-1][i];
          
          matrix[len][dig] = sum;
        
    
    return matrix[length-1][digit];


public int count(int index, int length, int[][] matrix )
    int sum = 0;
    if(matrix[length-1][index] > 0)
        System.out.println("getting value from memoize:"+index + "length:"+ length);
        return matrix[length-1][index];
    
    if( length == 1)
        return 1;
    
    for(int i: b[index] )  
         sum += count(i, length-1,matrix);
    
    matrix[length-1][index] = sum;
    return sum;

【讨论】:

【参考方案6】:

我决定解决这个问题并尽可能使其具有可扩展性。该解决方案允许您:

定义您自己的棋盘(电话板、棋盘等)

定义自己的棋子(Knight、Rook、Bishop 等);您必须编写具体类并从工厂生成它。

通过一些有用的实用方法检索多条信息。

类如下:

PadNumber:定义电话板上的按钮的类。可以重命名为“Square”以表示棋盘格。

ChessPiece:为所有棋子定义字段的抽象类。

Movement:定义运动方法并允许工厂生成部件的接口。

PieceFactory:生成棋子的工厂类。

Knight:继承自 ChessPiece 并实现 Movement 的具体类

PhoneChess:入门级。

驱动程序:驱动程序代码。

好的,这是代码:)

package PhoneChess;

import java.awt.Point;

public class PadNumber 

private String number = "";
private Point coordinates = null;

public PadNumber(String number, Point coordinates)

    if(number != null && number.isEmpty()==false)
        this.number = number;
    else
        throw new IllegalArgumentException("Input cannot be null or empty.");

    if(coordinates == null || coordinates.x < 0 || coordinates.y < 0)
        throw new IllegalArgumentException();
    else
        this.coordinates = coordinates;



public String getNumber()

    return this.number;

public Integer getNumberAsNumber()

    return Integer.parseInt(this.number);


public Point getCoordinates()

    return this.coordinates;

public int getX()

    return this.coordinates.x;

public int getY()

    return this.coordinates.y;



棋子

package PhoneChess;

import java.util.HashMap;
import java.util.List;

public abstract class ChessPiece implements Movement 

protected String name = "";
protected HashMap<PadNumber, List<PadNumber>> moves = null;
protected Integer fullNumbers = 0;
protected int[] movesFrom = null;
protected PadNumber[][] thePad = null;

运动界面:

package PhoneChess;

import java.util.List;

public interface Movement 

public Integer findNumbers(PadNumber start, Integer digits);
public abstract boolean canMove(PadNumber from, PadNumber to);
public List<PadNumber> allowedMoves(PadNumber from);
public Integer countAllowedMoves(PadNumber from);

片厂

package PhoneChess;

public class PieceFactory 

    public ChessPiece getPiece(String piece, PadNumber[][] thePad)
    
    if(thePad == null || thePad.length == 0 || thePad[0].length == 0)
        throw new IllegalArgumentException("Invalid pad");
    if(piece == null)
        throw new IllegalArgumentException("Invalid chess piece");

    if(piece.equalsIgnoreCase("Knight"))
        return new Knight("Knight", thePad);
    else
        return null;


骑士职业

package PhoneChess;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public final class Knight extends ChessPiece implements Movement 

/**Knight movements
 * One horizontal, followed by two vertical
 * Or 
 * One vertical, followed by two horizontal
 * @param name
 */

public Knight(String name, PadNumber[][] thePad)

    if(name == null || name.isEmpty() == true)
        throw new IllegalArgumentException("Name cannot be null or empty");

    this.name = name;
    this.thePad = thePad;
    this.moves = new HashMap<>();



private Integer fullNumbers = null;

@Override
public Integer findNumbers(PadNumber start, Integer digits) 

    if(start == null || "*".equals(start.getNumber()) || "#".equals(start.getNumber()) )  throw new IllegalArgumentException("Invalid start point"); 
    if(start.getNumberAsNumber() == 5)  return 0;  //Consider adding an 'allowSpecialChars' condition
    if(digits == 1)  return 1; ;

    //Init
    this.movesFrom = new int[thePad.length * thePad[0].length];
    for(int i = 0; i < this.movesFrom.length; i++)
        this.movesFrom[i] = -1;

    fullNumbers = 0;
    findNumbers(start, digits, 1);      
    return fullNumbers;


private void findNumbers(PadNumber start, Integer digits, Integer currentDigits)

    //Base condition
    if(currentDigits == digits)
    
        //Reset
        currentDigits = 1; 
        fullNumbers++; 
        return; 
    
    if(!this.moves.containsKey(start))
        allowedMoves(start);

    List<PadNumber> options = this.moves.get(start);
    if(options != null)
    
        currentDigits++; //More digits to be got
        for(PadNumber option : options)
            findNumbers(option, digits, currentDigits);
    


@Override
public boolean canMove(PadNumber from, PadNumber to) 

    //Is the moves list available?
    if(!this.moves.containsKey(from.getNumber()))
    
        //No? Process.
        allowedMoves(from);
    
    if(this.moves.get(from) != null)
    
        for(PadNumber option : this.moves.get(from))
        
            if(option.getNumber().equals(to.getNumber()))
                return true;
        
    
    return false;



/***
 * Overriden method that defines each Piece's movement restrictions.
 */
@Override
public List<PadNumber> allowedMoves(PadNumber from) 

    //First encounter
    if(this.moves == null)
        this.moves = new HashMap<>();


    if(this.moves.containsKey(from))
        return this.moves.get(from);
    else
    
        List<PadNumber> found = new ArrayList<>();
        int row = from.getY();//rows
        int col = from.getX();//columns

        //Cases:
        //1. One horizontal move each way followed by two vertical moves each way
        if(col-1 >= 0 && row-2 >= 0)//valid
        
            if(thePad[row-2][col-1].getNumber().equals("*") == false && 
                    thePad[row-2][col-1].getNumber().equals("#") == false)
            
                found.add(thePad[row-2][col-1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            

        
        if(col-1 >= 0 && row+2 < thePad.length)//valid
        
            if(thePad[row+2][col-1].getNumber().equals("*") == false && 
                    thePad[row+2][col-1].getNumber().equals("#") == false)
            
                found.add(thePad[row+2][col-1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            
        
        if(col+1 < thePad[0].length && row+2 < thePad.length)//valid
        
            if(thePad[row+2][col+1].getNumber().equals("*") == false && 
                    thePad[row+2][col+1].getNumber().equals("#") == false)
            
                found.add(thePad[row+2][col+1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            
        
        if(col+1 < thePad[0].length && row-2 >= 0)//valid
        
            if(thePad[row-2][col+1].getNumber().equals("*") == false && 
                    thePad[row-2][col+1].getNumber().equals("#") == false)
            found.add(thePad[row-2][col+1]);
        
        //Case 2. One vertical move each way follow by two horizontal moves each way

        if(col-2 >= 0 && row-1 >= 0)
        
            if(thePad[row-1][col-2].getNumber().equals("*") == false && 
                    thePad[row-1][col-2].getNumber().equals("#") == false)
            found.add(thePad[row-1][col-2]);
        
        if(col-2 >= 0 && row+1 < thePad.length)
        
            if(thePad[row+1][col-2].getNumber().equals("*") == false && 
                    thePad[row+1][col-2].getNumber().equals("#") == false)
            found.add(thePad[row+1][col-2]);
        

        if(col+2 < thePad[0].length && row-1 >= 0)
        
            if(thePad[row-1][col+2].getNumber().equals("*") == false && 
                    thePad[row-1][col+2].getNumber().equals("#") == false)
            found.add(thePad[row-1][col+2]);
        
        if(col+2 < thePad[0].length && row+1 < thePad.length)
        
            if(thePad[row+1][col+2].getNumber().equals("*") == false && 
                    thePad[row+1][col+2].getNumber().equals("#") == false)
            found.add(thePad[row+1][col+2]);
        

        if(found.size() > 0)
        
            this.moves.put(from, found);
            this.movesFrom[from.getNumberAsNumber()] = found.size();
        
        else
        
            this.moves.put(from, null); //for example the Knight cannot move from 5 to anywhere
            this.movesFrom[from.getNumberAsNumber()] = 0;
        
    

    return this.moves.get(from);




@Override
public Integer countAllowedMoves(PadNumber from) 

    int start = from.getNumberAsNumber();

    if(movesFrom[start] != -1)
        return movesFrom[start];
    else
    
        movesFrom[start] = allowedMoves(from).size();
    
    return movesFrom[start];


@Override
public String toString()

    return this.name;



PhoneChess 参赛班

package PhoneChess;


public final class PhoneChess 

private ChessPiece thePiece = null;
private PieceFactory factory = null;

public ChessPiece ThePiece()

    return this.thePiece;


public PhoneChess(PadNumber[][] thePad, String piece)

    if(thePad == null || thePad.length == 0 || thePad[0].length == 0)
        throw new IllegalArgumentException("Invalid pad");
    if(piece == null)
        throw new IllegalArgumentException("Invalid chess piece");

    this.factory = new PieceFactory();
    this.thePiece = this.factory.getPiece(piece, thePad);


public Integer findPossibleDigits(PadNumber start, Integer digits)

    if(digits <= 0)
        throw new IllegalArgumentException("Digits cannot be less than or equal to zero");

    return thePiece.findNumbers(start, digits);


public boolean isValidMove(PadNumber from, PadNumber to)

    return this.thePiece.canMove(from, to);



驱动代码:

public static void main(String[] args) 


    PadNumber[][] thePad = new PadNumber[4][3];
    thePad[0][0] = new PadNumber("1", new Point(0,0));
    thePad[0][1] = new PadNumber("2", new Point(1,0));
    thePad[0][2] = new PadNumber("3",new Point(2,0));
    thePad[1][0] = new PadNumber("4",new Point(0,1));
    thePad[1][1] = new PadNumber("5",new Point(1,1));
    thePad[1][2] = new PadNumber("6", new Point(2,1));
    thePad[2][0] = new PadNumber("7", new Point(0,2));
    thePad[2][1] = new PadNumber("8", new Point(1,2));
    thePad[2][2] = new PadNumber("9", new Point(2,2));
    thePad[3][0] = new PadNumber("*", new Point(0,3));
    thePad[3][1] = new PadNumber("0", new Point(1,3));
    thePad[3][2] = new PadNumber("#", new Point(2,3));

    PhoneChess phoneChess = new PhoneChess(thePad, "Knight");
    System.out.println(phoneChess.findPossibleDigits(thePad[0][1],4));



【讨论】:

【参考方案7】:

Java 中的递归函数:

public static int countPhoneNumbers (int n, int r, int c) 
        if (outOfBounds(r,c)) 
            return 0;
         else 
            char button = buttons[r][c];
            if (button  == '.') 
                // visited
                return 0;
              else 
                buttons[r][c] = '.'; // record this position so don't revisit.
                // Count all possible phone numbers with one less digit starting
                int result=0;
                result = countPhoneNumbers(n-1,r-2,c-1)
                                         + countPhoneNumbers(n-1,r-2,c+1)
                                         + countPhoneNumbers(n-1,r+2,c-1)
                                         + countPhoneNumbers(n-1,r+2,c+1)
                                         + countPhoneNumbers(n-1,r-1,c-2)
                                         + countPhoneNumbers(n-1,r-1,c+2)
                                         + countPhoneNumbers(n-1,r+1,c-2)
                                         + countPhoneNumbers(n-1,r+1,c+2);
                
                buttons[r][c] = button; // Remove record from position.
                return result; 
            
        
    

【讨论】:

【参考方案8】:

运行时间常数时间解:

#include <iostream>

constexpr int notValid(int x, int y) 
return !(( 1 == x && 3 == y ) || //zero on bottom.
         ( 0 <= x && 3 > x && //1-9
           0 <= y && 3 > y ));


class Knight 
    template<unsigned N > constexpr int move(int x, int y) 
        return notValid(x,y)? 0 : jump<N-1>(x,y);
    

    template<unsigned N> constexpr int jump( int x, int y ) 
        return  move<N>(x+1, y-2) +
            move<N>(x-1, y-2) +
            move<N>(x+1, y+2) +
            move<N>(x-1, y+2) +
            move<N>(x+2, y+1) +
            move<N>(x-2, y+1) +
            move<N>(x+2, y-1) +
            move<N>(x-2, y-1);
    

public:
    template<unsigned N> constexpr int count() 
        return move<N-1>(0,1) + move<N-1>(0,2) +
            move<N-1>(1,0) + move<N-1>(1,1) + move<N-1>(1,2) +
            move<N-1>(2,0) + move<N-1>(2,1) + move<N-1>(2,2);
    
;

template<> constexpr int Knight::move<0>(int x, int y)  return notValid(x,y)? 0 : 1; 
template<> constexpr int Knight::count<0>()  return 0;  //terminal cases.
template<> constexpr int Knight::count<1>()  return 8; 


int main(int argc, char* argv[]) 
    static_assert( ( 16 == Knight().count<2>() ), "Fail on test with 2 lenght" );  // prof of performance
    static_assert( ( 35 == Knight().count<3>() ), "Fail on test with 3 lenght" );

    std::cout<< "Number of valid Knight phones numbers:" << Knight().count<10>() << std::endl;
    return 0;

【讨论】:

好吧,如果你直接在代码中输入位数(10),你也可以马上做std::cout &lt;&lt; 1424 &lt;&lt; std::endl;。 :-)(如果您从标准输入读取 N,我认为此解决方案不起作用。) @aioobe 如果您将 N 提供给编译器标准输入,它应该可以工作:)【参考方案9】:

当然可以在多项式时间内完成。这是dynamic programming 或memoization 的绝佳练习。

假设 N(位数)在示例中等于 10。

这样递归思考:1开始,我可以用 10 位数字构造多少个数字?

答案是

[number of 9-digit numbers starting from 8] +
[number of 9-digit numbers starting from 6].

那么有多少个“从 8 开始的 9 位数字”?嗯,

[number of 8-digit numbers starting from 1] +
[number of 8-digit numbers starting from 3]

等等。当您收到问题“从X 开始有多少个 1 位数字”(答案显然是 1)时,就达到了基本情况。

谈到复杂性时,关键的观察是您重复使用以前计算的解决方案。即例如“有多少个从3开始的5位数字”的答案,可以在回答“有多少个6位数字开头来自8" AND "从4开始有多少个6位数字"。这种重用使得复杂性从指数级崩溃到多项式。

让我们仔细看看动态规划解决方案的复杂性:

这样的实现会按以下方式填充矩阵:

num[1][i] = 1, for all 0<=i<=9   -- there are one 1-digit number starting from X.

for digits = 2...N
    for from = 0...9
        num[digits][from] = num[digits-1][successor 1 of from] +
                            num[digits-1][successor 2 of from] +
                            ...
                            num[digits-1][successor K of from]

return num[N][1]                 -- number of N-digit numbers starting from 1.

该算法简单地一次填充一个单元格,矩阵的维度为10*N,因此在线性时间中运行。


从头顶上写下来,如有错别字请指正。

【讨论】:

使用您的算法的工作解决方案(使用 Python3)github.com/harishvc/challenges/blob/master/… 。您能否进一步解释一下线性时间复杂度? @aioobe,因为我们根据前一个计算缓存中的当前行,所以我们可以只使用 int[10] 并且我们得到 O(1) 空间。【参考方案10】:

这个问题也可以建模为Constraint satisfaction problem(简称CSP)。

我建议使用您可以找到here 的 Minion 求解器(快速且可扩展)。

建模可能繁琐且耗时(学习曲线陡峭)。

我的建议是使用独立于求解器的建模语言(例如 ESSENCE)来制定模型,而不是使用 Minion 语言输入,并找到相应的转换器。

【讨论】:

【参考方案11】:

一个更简单的答案。

#include<stdio.h>

int a[10] = 2,2,2,2,3,0,3,2,2,2;
int b[10][3] = 4,6,6,8,7,9,4,8,0,3,9,,1,7,0,2,6,1,3,2,4;

int count(int curr,int n)

    int sum = 0;
    if(n==10)
        return 1;
    else
    
        int i = 0;
        int val = 0;
        for(i = 0; i < a[curr]; i++)
        
            val = count(b[curr][i],n+1);
            sum += val;
        
        return sum;
    


int main()

    int n = 1;
    int val = count(1,0);
    printf("%d\n",val);

庆祝!!

【讨论】:

不应该 n==10 改为 9,因为您必须以 1 作为第一个数字开始,并且只需要 9 个数字即可达到 10 位数字?【参考方案12】:

这可以在 O(log N) 内完成。将键盘及其上可能的移动视为图 G(V, E),其中顶点是可用数字,边表示哪些数字可以跟随哪个数字。现在对于每个输出位置 i 我们可以形成一个向量 Paths(i) 包含每个顶点可以到达的不同路径的数量。现在很容易看到给定位置 i 和数字 v,它可以到达的可能路径是可能到达前面数字的不同路径的总和,或 Paths(i)[v] = sum(Paths(i-1)[v2] * (1 if (v,v2) in E else 0) for v2 in V)。现在,这是将前一个向量的每个位置的总和乘以邻接矩阵的列中的相应位置。所以我们可以将其简化为Paths(i) = Paths(i-1)·A,其中A是图的邻接矩阵。摆脱递归并利用矩阵乘法的结合性,这变为 Paths(i) = Paths(1) · A^(i-1)。我们知道Paths(1):我们只有一条路径,到达数字 1。

n位数字的路径总数是每个数字的路径之和,因此最终算法变为:TotalPaths(n) = sum( [1,0,0,0,0, 0,0,0,0,0] · A^(n-1) )

幂可以通过在O(log(n))时间内求平方来计算,给定时间乘以常数,否则O(M(n) * log(n)) em> 其中 M(n) 是您最喜欢的 n 个数字的任意精度乘法算法的复杂度。

【讨论】:

很好地使用了邻接矩阵。但请注意,您认为 N 是电话号码的长度,而在问题中,复杂性在于可用位数。这样,你得到 O(n^2) 来计算 A 的 10 次方。 不,我认为 N 应该是电话号码的长度。小键盘应该如何查找更多的数字? 具有任意移动的任意大小键盘的复杂性将基于朴素的稀疏矩阵乘法 O(dmlog n),其中 d 是位数,m 是可能移动的总数(m 其实这个解可以在关于N的常数时间内运行。由于A是对称的,所以可以对角化,然后取A^N就变成了取10个实数的N次方的问题可以假定为常数时间,并执行一些独立于 N 的其他操作。

以上是关于使用电话键盘生成 10 位数字的主要内容,如果未能解决你的问题,请参考以下文章

仅类似于电话的数字软键盘

如苹果电话应用程序所示,创建数字键盘的最佳方法

c语言从键盘上输入一个4位整数,输出其个位、十位、百位、千位上的数字,并求和。(代码15分,调试1

HTML5 输入类型号与电话

c语言高手急救:从键盘输入长整数n,将其从个位开始,每三位数字一组用逗号间隔输出。

在 ipad 上强制数字键盘用于 contenteditable 元素