【问题标题】:Find all possible combinations of a String representation of a number查找数字的字符串表示形式的所有可能组合
【发布时间】:2013-07-31 05:52:47
【问题描述】:

给定一个映射:

A: 1
B: 2
C: 3
...
...
...
Z: 26

找出可以表示数字的所有可能方式。例如。对于输入:“121”,我们可以将其表示为:

ABA [using: 1 2 1]
LA [using: 12 1]
AU [using: 1 21]

我尝试考虑使用某种动态编程方法,但我不确定如何继续。我在一次技术面试中被问到这个问题。

这是我能想到的解决方案,如果看起来不错,请告诉我:

A[i]: Total number of ways to represent the sub-array number[0..i-1] using the integer to alphabet mapping.

解决方案[我错过了什么吗?]:

A[0] = 1 // there is only 1 way to represent the subarray consisting of only 1 number
for(i = 1:A.size):
    A[i] = A[i-1]
    if(input[i-1]*10 + input[i] < 26):
        A[i] += 1
    end
end
print A[A.size-1]

【问题讨论】:

  • 您是否必须打印所有可能的组合或可能的组合数量?
  • 如果输入是101呢?它是否分为 10,1 和 1,01?
  • @Fallen: 可能组合的数量
  • 杰森,你是对的。

标签: algorithm dynamic combinations


【解决方案1】:

为了获得计数,动态编程方法非常简单:

A[0] = 1
for i = 1:n
  A[i] = 0
  if input[i-1] > 0                            // avoid 0
    A[i] += A[i-1];
  if i > 1 &&                          // avoid index-out-of-bounds on i = 1
      10 <= (10*input[i-2] + input[i-1]) <= 26 // check that number is 10-26
    A[i] += A[i-2];

如果您想要列出所有表示,动态编程并不是特别适合此,您最好使用简单的递归算法。

【讨论】:

  • 杜克林,你的解决方案让我开始思考正确的方向。我确实,对您代码中的主要逻辑有一些反对意见。为什么要查看 [i-2] 和 [i-1] 处的元素?您不应该查看 input[i-1] 和 input[i] 来检查 input[i-1...i] 是否位于 [1,26] 中吗?我已经更新了我的问题,以提出一个我可以根据您的代码在这里想到的解决方案。你能评论一下吗?
  • i-2i-1i-1i 基本相同。对于我的解决方案,A[0] 用于包含 0 个元素的数组,您的用于 1 个元素,这两种方法都有效。对于您的解决方案 - A[i-1]*10 + A[i] 应该是 input[i-1]*10 + input[i]A 是您的 DP 数组,而不是输入。对于109,您将计算009,但两者都无效 - 您需要包括额外的检查(就像我所做的那样)。 A[i] += 1 不正确。不能只增加 1,例如对于 1912,您的 A 将是 [1,2,2,3],但应该是 [1,2,2,4],您需要使用少 2 个字符来添加表示的数量。
  • 很好的解释。根据我们下面的讨论,我已经针对这个问题发布了一个基于 Java 的解决方案。
  • Answer 确实计算了带有 leading zero 或超过 1 个前导零的解决方案:1001。
【解决方案2】:

首先,我们需要找到一种直观的方式来列举所有的可能性。我的简单构造,如下所示。

 let us assume a simple way to represent your integer in string format.

   a1 a2 a3 a4 ....an, for instance in 121 a1 -> 1 a2 -> 2, a3 -> 1

现在,

我们需要找出在两个字符之间放置 + 号的可能性。 + 在这里表示字符连接。

a1 - a2 - a3 - .... - an, - shows the places where '+' can be placed. So, number of positions is n - 1, where n is the string length. 

假设一个位置可能有也可能没有 + 符号应表示为位。 因此,这归结为长度为 n-1 的可能有多少不同的位串,这显然是 2^(n-1)。现在为了枚举可能性,遍历每个位串并将右 + 号放在相应的位置以获得每个表示,

例如,121

   Four bit strings are possible 00 01 10 11
   1   2   1
   1   2 + 1
   1 + 2   1
   1 + 2 + 1

  And if you see a character followed by a +, just add the next char with the current one and do it sequentially to get the representation,

 x + y z a + b + c d

 would be (x+y) z (a+b+c) d  

希望对你有帮助。

当然,您必须处理某些整数大小 > 26 的极端情况。

【讨论】:

    【解决方案3】:

    我认为,递归遍历所有可能的组合就可以了:

    mapping = {"1":"A", "2":"B", "3":"C", "4":"D", "5":"E", "6":"F", "7":"G", 
    "8":"H", "9":"I", "10":"J", 
    "11":"K", "12":"L", "13":"M", "14":"N", "15":"O", "16":"P", 
    "17":"Q", "18":"R", "19":"S", "20":"T", "21":"U", "22":"V", "23":"W", 
    "24":"A", "25":"Y", "26":"Z"}
    
    def represent(A, B):
        if A == B == '':
            return [""]
        ret = []
        if A in mapping:
            ret += [mapping[A] + r for r in represent(B, '')]
        if len(A) > 1:
            ret += represent(A[:-1], A[-1]+B)
        return ret
    
    print represent("121", "")
    

    【讨论】:

      【解决方案4】:

      假设您只需要计算组合的数量。

      假设 [1,9] 中的 0 后跟一个整数不是有效的串联,那么暴力策略将是:

      Count(s,n)
          x=0
          if (s[n-1] is valid)
              x=Count(s,n-1)
          y=0
          if (s[n-2] concat s[n-1] is valid)
              y=Count(s,n-2)
          return x+y
      

      更好的策略是使用分而治之:

      Count(s,start,n)
          if (len is even)
          {
              //split s into equal left and right part, total count is left count multiply right count
              x=Count(s,start,n/2) + Count(s,start+n/2,n/2);
              y=0;
              if (s[start+len/2-1] concat s[start+len/2] is valid)
              {
                  //if middle two charaters concatenation is valid
                  //count left of the middle two characters
                  //count right of the middle two characters
                  //multiply the two counts and add to existing count
                  y=Count(s,start,len/2-1)*Count(s,start+len/2+1,len/2-1);
              }
              return x+y;
          }
          else
          {
              //there are three cases here:
      
              //case 1: if middle character is valid, 
              //then count everything to the left of the middle character, 
              //count everything to the right of the middle character,
              //multiply the two, assign to x
              x=...
      
              //case 2: if middle character concatenates the one to the left is valid, 
              //then count everything to the left of these two characters
              //count everything to the right of these two characters
              //multiply the two, assign to y
              y=...
      
              //case 3: if middle character concatenates the one to the right is valid, 
              //then count everything to the left of these two characters
              //count everything to the right of these two characters
              //multiply the two, assign to z
              z=...
      
              return x+y+z;
          }
      

      蛮力解决方案的时间复杂度为T(n)=T(n-1)+T(n-2)+O(1),这是指数级的。

      分治法的时间复杂度为T(n)=3T(n/2)+O(1),即O(n**lg3)。

      希望这是正确的。

      【讨论】:

        【解决方案5】:

        这样的?

        Haskell 代码:

        import qualified Data.Map as M
        import Data.Maybe (fromJust)
        
        combs str = f str [] where
          charMap = M.fromList $ zip (map show [1..]) ['A'..'Z']
          f []     result = [reverse result]
          f (x:xs) result
            | null xs = 
                case M.lookup [x] charMap of
                  Nothing -> ["The character " ++ [x] ++ " is not in the map."]
                  Just a  -> [reverse $ a:result]
            | otherwise = 
                case M.lookup [x,head xs] charMap of
                  Just a  -> f (tail xs) (a:result) 
                         ++ (f xs ((fromJust $ M.lookup [x] charMap):result))
                  Nothing -> case M.lookup [x] charMap of
                               Nothing -> ["The character " ++ [x] 
                                        ++ " is not in the map."]
                               Just a  -> f xs (a:result)
        

        输出:

        *Main> combs "121"
        ["LA","AU","ABA"]
        

        【讨论】:

          【解决方案6】:

          这是基于我在这里讨论的解决方案:

          private static int decoder2(int[] input) {
              int[] A = new int[input.length + 1];
              A[0] = 1;
          
              for(int i=1; i<input.length+1; i++) {
                A[i] = 0;
                if(input[i-1] > 0) {
                  A[i] += A[i-1];
                }
                if (i > 1 && (10*input[i-2] + input[i-1]) <= 26) {
                  A[i] += A[i-2];
                }
                System.out.println(A[i]);
              }
              return A[input.length];
          }
          

          【讨论】:

            【解决方案7】:

            经过研究,我偶然发现了这个视频https://www.youtube.com/watch?v=qli-JCrSwuk,解释得很好。

            【讨论】:

            • 虽然视频回答了这个问题。最好将解决方案包含在答案中。视频将来也可能会被删除
            【解决方案8】:

            只有我们广度优先搜索。

            例如 121

            从第一个整数开始, 首先考虑 1 个整数字符,将 1 映射到 a,留下 21 然后 2 个整数字符映射 12 到 L 离开 1。

            【讨论】:

              【解决方案9】:

              这个问题可以用标准的 DP 算法在 o(fib(n+2)) 时间内完成。 我们正好有 n 个子问题,而且我们可以在 o(fib(i)) 时间内解决每个大小为 i 的问题。 将级数相加得到 fib (n+2)。

              如果您仔细考虑这个问题,您会发现它是一个斐波那契数列。 我采用了一个标准的斐波那契代码,并对其进行了更改以适应我们的条件。

              空间显然与所有解的大小 o(fib(n)) 绑定。

              考虑这个伪代码:

              Map<Integer, String> mapping = new HashMap<Integer, String>();
              
              List<String > iterative_fib_sequence(string input) {
                  int length = input.length;
                  if (length <= 1) 
                  {
                      if (length==0)
                      {
                          return "";
                      }
                      else//input is a-j
                      {
                          return mapping.get(input);
                      }
                  }
                  List<String> b = new List<String>();
                  List<String> a = new List<String>(mapping.get(input.substring(0,0));
                  List<String> c = new List<String>();
              
                  for (int i = 1; i < length; ++i) 
                  {
                      int dig2Prefix = input.substring(i-1, i); //Get a letter with 2 digit (k-z)
                      if (mapping.contains(dig2Prefix))
                      {
                          String word2Prefix = mapping.get(dig2Prefix);           
                          foreach (String s in b)
                          {
                              c.Add(s.append(word2Prefix));
                          }
                      }
              
                      int dig1Prefix = input.substring(i, i); //Get a letter with 1 digit (a-j)
                      String word1Prefix = mapping.get(dig1Prefix);           
                      foreach (String s in a)
                      {
                          c.Add(s.append(word1Prefix));
                      }
              
                      b = a;
                      a = c;
                      c = new List<String>();
                  }
                  return a;
              }
              

              【讨论】:

                【解决方案10】:

                旧问题,但添加了一个答案以便人们可以找到帮助

                我花了一些时间来理解这个问题的解决方案——我参考了接受的答案和@Karthikeyan 的答案以及来自geeksforgeeks 的解决方案,并编写了我自己的代码如下:

                要了解我的代码,请先了解以下示例:

                • 我们知道,decodings([1, 2])"AB""L" 等等 decoding_counts([1, 2]) == 2
                • 并且,decodings([1, 2, 1])"ABA""AU""LA" 等等 decoding_counts([1, 2, 1]) == 3

                使用上面两个例子让我们评估decodings([1, 2, 1, 4])

                • 案例:-“以下一位为单位”

                  4 作为单个数字解码为字母'D',我们得到decodings([1, 2, 1, 4]) == decoding_counts([1, 2, 1]),因为[1, 2, 1, 4] 将被解码为"ABAD""AUD" , "LAD"

                • case:-“将下一位与前一位结合”

                  4 与之前的1 组合为14 以解码为字母N,我们得到decodings([1, 2, 1, 4]) == decoding_counts([1, 2]),因为[1, 2, 1, 4] 将是解码为"ABN""LN"

                下面是我的 Python 代码,请阅读 cmets

                def decoding_counts(digits):
                    # defininig count as, counts[i] -> decoding_counts(digits[: i+1])
                    counts = [0] * len(digits)
                
                    counts[0] = 1
                    for i in xrange(1, len(digits)):
                
                        # case:- "taking next digit as single digit"
                        if digits[i] != 0: # `0` do not have mapping to any letter
                            counts[i] = counts[i -1]
                
                        # case:- "combining next digit with the previous digit"
                        combine = 10 * digits[i - 1] + digits[i]
                        if 10 <= combine <= 26: # two digits mappings
                            counts[i] += (1 if i < 2 else counts[i-2])
                
                    return counts[-1]
                
                for digits in "13", "121", "1214", "1234121":
                    print digits, "-->", decoding_counts(map(int, digits))
                

                输出:

                13 --> 2
                121 --> 3
                1214 --> 5
                1234121 --> 9
                

                注意:我假设输入 digits 不以 0 开头,仅包含 0-9 并且有足够的长度

                【讨论】:

                • 注:decoding_counts([1, 3, 0]) --&gt; 0 !!这是一个错误
                • 如果有人想要"1214" 根据decoding_counts() 的完整执行示例,请告诉我
                【解决方案11】:

                对于 Swift,这就是我想出的。基本上,我将字符串转换为一个数组并遍历它,在该数组的不同位置添加一个空格,然后将它们附加到另一个数组中用于第二部分,完成后应该很容易。

                //test case
                let input = [1,2,2,1]
                
                func combination(_ input: String) {
                    var arr = Array(input)
                    var possible = [String]()
                
                    //... means inclusive range
                    for i in 2...arr.count {
                        var temp = arr
                
                        //basically goes through it backwards so 
                        //  adding the space doesn't mess up the index
                        for j in (1..<i).reversed() {
                            temp.insert(" ", at: j)
                            possible.append(String(temp))
                        }
                    }
                    print(possible)
                }
                
                combination(input)
                
                //prints: 
                //["1 221", "12 21", "1 2 21", "122 1", "12 2 1", "1 2 2 1"]
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-11-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-05-06
                  • 1970-01-01
                  • 2017-01-02
                  相关资源
                  最近更新 更多