时间优化C ++函数以查找解码可能性的数量

时间:2017-04-18 01:03:26

标签: c++ runtime dynamic-programming decoding string-decoding

这是interview practice problem from CodeFights。我有一个解决方案可以工作,除了运行非常大的输入需要太长时间。

问题描述(来自上面的链接)

包含message'A'的大写字母的绝密'Z'已使用以下映射编码为数字:

'A' -> 1
'B' -> 2
...
'Z' -> 26

您是FBI代理人,您需要确定消息可以解码的总方式。

由于答案可能非常大,因此请将其模数为10 ^ 9 + 7。

实施例

对于message = "123",输出应为

mapDecoding(message) = 3

"123"可以解码为"ABC" (1 2 3)"LC" (12 3)"AW" (1 23),因此总数为方式是3

输入/输出

[时间限制] 500毫秒(cpp)

[输入]字符串消息

仅包含数字的字符串。

保证约束:

0 ≤ message.length ≤ 105

[输出]整数

解码给定消息的总方式。

到目前为止我的解决方案

我们必须在函数int mapDecoding(std::string message)中实现解决方案,所以我的整个解决方案如下:

/*0*/ void countValidPaths(int stIx, int endIx, std::string message, long *numPaths)
/*1*/ {
/*2*/    //check out-of-bounds error
/*3*/    if (endIx >= message.length())
/*4*/        return;
/*5*/    
/*6*/    int subNum = 0, curCharNum;
/*7*/    //convert substr to int
/*8*/    for (int i = stIx; i <= endIx; ++i)
/*9*/    {
/*10*/       curCharNum = message[i] - '0';
/*11*/       subNum = subNum * 10 + curCharNum;
/*12*/   }
/*13*/    
/*14*/   //check for leading 0 in two-digit number, which would not be valid
/*15*/   if (endIx > stIx && subNum < 10)
/*16*/       return;
/*17*/    
/*18*/   //if number is valid
/*19*/   if (subNum <= 26 && subNum >= 1)
/*20*/   {
/*21*/       //we've reached the end of the string with success, therefore return a 1
/*22*/       if (endIx == (message.length() - 1) )
/*23*/           ++(*numPaths);
/*24*/       //branch out into the next 1- and 2-digit combos
/*25*/       else if (endIx == stIx)
/*26*/       {
/*27*/           countValidPaths(stIx, endIx + 1, message, numPaths);
/*28*/           countValidPaths(stIx + 1, endIx + 1, message, numPaths);
/*29*/       }
/*30*/       //proceed to the next digit
/*31*/       else
/*32*/            countValidPaths(endIx + 1, endIx + 1, message, numPaths);
/*33*/   }
/*34*/ }    
/*35*/
/*36*/ int mapDecoding(std::string message) 
/*37*/ {
/*38*/    if (message == "")
/*39*/        return 1;
/*40*/    long numPaths = 0;
/*41*/    int modByThis = static_cast<int>(std::pow(10.0, 9.0) + 7);
/*42*/    countValidPaths(0, 0, message, &numPaths);
/*43*/    return static_cast<int> (numPaths % modByThis);
/*44*/ }

问题

我已经通过了CodeFight的初始测试用例的11/12,例如mapDecoding("123") = 3mapDecoding("11115112112") = 104。但是,最后一个测试用例有message = "1221112111122221211221221212212212111221222212122221222112122212121212221212122221211112212212211211",我的程序执行时间太长:

Expected_output:      782204094
My_program_output:    <empty due to timeout>

我写了countValidPaths()作为递归函数,它的递归调用在第27,28和32行。我可以看到这么大的输入会导致代码花费这么长时间,但是我可以看到。我绞尽脑汁试图弄清楚哪些更有效的解决方案将涵盖所有可能的组合。

因此,百万美元的问题:你有什么建议来优化我当前的程序,以便它在更短的时间内运行?

1 个答案:

答案 0 :(得分:1)

一些建议。 首先,这个问题可能被描述为动态规划问题。它有这种气味给我。你一遍又一遍地计算同样的东西。

第二个是洞察力,就可能性的数量而言,“1”和“2”的长连续序列是斐波那契序列。任何其他值都会终止序列。因此,您可以将字符串拆分为1和1的运行,并以任何其他数字终止。您将需要特殊的逻辑来终止零,因为它也不对应于一个字符。因此,拆分字符串计数,每个段的长度,查找斐波纳契数(可以预先计算)并乘以值。所以你的例子“11115112112”产生“11115”和“112112”,f(5)= 8,f(6)= 13,8 * 13 = 104。

您的长字符串是1和2的序列,长度为100位。以下Java(抱歉,我的C ++生锈)程序通过此方法正确计算其值

public class FibPaths {
private static final int MAX_LEN = 105;
private static final BigInteger MOD_CONST = new BigInteger("1000000007");
private static BigInteger[] fibNum = new BigInteger[MAX_LEN];

private static void computeFibNums() {
    fibNum[0] = new BigInteger("1");
    fibNum[1] = new BigInteger("1");
    for (int i = 2; i < MAX_LEN; i++) {
        fibNum[i] = fibNum[i-2].add(fibNum[i-1]);
    }
}

public static void main(String[] argv) {
    String x = "1221112111122221211221221212212212111221222212122221222112122212121212221212122221211112212212211211";
    computeFibNums();
    BigInteger val = fibNum[x.length()].mod(MOD_CONST);
    System.out.println("N=" + x.length() + " , val = " + val);
}

}