Find the kth eleven-non-free number

前端 未结 8 2263
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-04 10:10

Let\'s define eleven-non-free numbers:

If we consider a number as a string, then if any substring inside is a (non-zero) power of 11, then this

8条回答
  •  南旧
    南旧 (楼主)
    2021-02-04 10:39

    As good starting point is to consider the related problem of counting how many eleven-non-free (ENF) integers there are of N digits. This is not exactly the same thing as finding the n'th ENF integer, but the latter problem can be reduced to the former pretty easily (see the end of this post for java code that does this).

    The idea is to do dynamic programming on prefixes. For any string of digits s, and any integer k, let N(k,s) denote the number of ENF strings of length k which start with s. And, for any string s, let s_0 be the longest suffix of s that also occurs as a prefix of any power of eleven (POE) of length k or less. Then, assuming that s itself does not contain any POE substrings, we have the equation:

    N(k,s) = N(k-length(s)+length(s_0),s_0)
    

    The reasoning behind this equation is as follows. Since s_0 is a suffix of s, let us write s as a concatenation of some other string q and s_0: s=[q,s_0]. Now, let r be any digit string which starts with s and again let us write this as a concatenation, as r=[s,r_0] = [q,s_0,r_0].

    If r contains a POE as a substring, then either this POE is contained in r_0, or else it crosses over the boundary between s and r_0. In this case, s must end in a POE prefix, and, since we know that the longest POE prefix that occurs as a suffix of s is s_0, it follows that if r contains a POE substring, then this substring must be contained in [s_0,r_0]. This gives us a one-to-one correspondence between ENFs that begin with s=[q,s_0] and ENFs that begin with s_0.

    This above equation gives rise to a recursive algorithm to count the number of k-digit ENFs with a given prefix. The base cases end up being instances where length(s_0)=length(s) which means that s itself is a prefix of a POE of length k or less. Since there aren't very many such POE prefixes (at most k choose 2 which is O(k^2)), this formula leads to an efficient algorithm. Here is pseudocode:

    Given a prefix s and an integer k, this function computes N(k,s)
    
    if s contains a POE then return 10^(k-length(s))
    if length(s) = k return 0 
    let s0 = longest POE prefix which is a suffix of s
    if(length(s0)

    Using dynamic programming, the running time of this algorithm will be polynomial in k, the number of digits. The algorithm only recurses on POE prefixes, and since there are O(k^2) of these, the running time will be O(k^2) times the running time of each iteration. Using a naive O(k^2) algorithm to find the longest suffix that matches a POE prefix would thus lead to an O(k^4) algorithm, while using a radix tree to find matches in linear time would result in O(k^3). The java code given below uses the naive algorithm, and experimentally, for values up to around k=100 seems to be Θ(k^4.5), though this discrepancy could well be due to implementation details or constant factors affecting runtimes for small input sizes. Here is the code:

    public class Eleven {
    
      public static final String[] digits = 
        {"0","1","2","3","4","5","6","7","8","9"};
    
      public static BigInteger countENF(String prefix, int k){
        return countENF(prefix,k,new HashMap(),getPowers(k));
      }
    
      public static BigInteger countENF(String prefix, 
          int k, 
          // This map contains stored results for dynamic programming
          // Pair is an auxiliary class that does what you think it does
          Map,BigInteger> resultMap,
          // precomputed list of powers of 11
          List powers
          ){
        // Dynamic programming case
        BigInteger res = resultMap.get(new Pair(prefix,k));
        if(res != null)
          return res;
    
        // base cases
        if(!isEFree(prefix, powers)){
          res = new BigInteger("10").pow(k-prefix.length());
          resultMap.put(new Pair<>(prefix,k), res);
          return res;
        }
        if(prefix.length() >= k){
          res = new BigInteger("0");
          resultMap.put(new Pair<>(prefix,k), res);
          return res;
        }    
        String s0 = getMaxPrefix(prefix, powers);
        if(s0.length() < prefix.length()){
          return countENF(s0,k+s0.length()-prefix.length(),resultMap,powers);
        }
    
        // recursive summation
        res = new BigInteger("0");
        for(String d : digits){
          String s1 = prefix + d;
          res = res.add(countENF(s1,k,resultMap,powers));
        }
        resultMap.put(new Pair<>(prefix, k), res);
        return res;
      }  
    
    
      //
      // helper functions
      //
    
      // returns all POEs of at most length digits
      private static List getPowers(int length) {
        List ret = new ArrayList<>();
        BigInteger val = new BigInteger("11");
        BigInteger eleven = new BigInteger("11");
        while(val.toString().length() <= length){
          ret.add(val.toString());
          val = val.multiply(eleven);
        }
        return ret;
      }
    
      // finds the longest string that is both a prefix of s and a suffix of a POE
      private static String getMaxSuffix(String s, List powers){
        for(int i=s.length(); i>0; i--){
          String sub = s.substring(0,i);
          for(String p : powers){
            if(p.endsWith(sub))
              return sub;
          }
        }
        return "";
      }
    
      public static boolean isEFree(String s, List powers){
        for(String p : powers){
          if(s.indexOf(p)>=0)
            return false;
        }
        return true;
      }
    

    As mentioned above, this algorithm does not solve the exact problem in the OP. But, modifying this code to actually find the n'th ENF number is pretty simple. Making repeated callse to countENF, we first figure out how many digits the n'th ENF number has, and then, one digit at a time, we figure out the digits of the n'th ENF from left to right.

     public static String getNthENF(BigInteger i){
        int k = i.toString().length();
        HashMap results = new HashMap();
        List powers = getPowers(k);
        while(countENF("",k,results,powers).compareTo(i) < 0){
          k++;
         powers = getPowers(k);
        }
        String solution = "";
        BigInteger total = new BigInteger("0");
    
        while(solution.length() < k){
          for(String d : digits){
            BigInteger newTotal = total.add(countENF(solution + d,k,results,powers));
            int comp = newTotal.compareTo(i);
            if(comp >= 0){
              solution = solution + d;
              break;
            }
            total = newTotal;
          }
        }
        return solution;
      }
    

    Here is some sample output, giving the N'th ENF as computed using dynamic programming, as well as using brute force, for powers of 10.

    Dynamic Programming:
    10^1: 118
    10^2: 1178
    10^3: 11680
    10^4: 115730
    10^5: 1146628
    10^6: 11360558
    10^7: 112558960
    10^8: 1115229050
    10^9: 11049731548
    10^10: 103258311161
    10^11: 935443232311
    10^12: 8576360477119
    10^13: 79330786951511
    10^14: 732117130575070
    10^15: 6880811638385388
    10^16: 64284911460844887
    10^17: 610616803411054857
    10^18: 5759459802926457113
    10^19: 54555977711878792498
    10^20: 518773721711219891634
    Brute Force:
    10^1: 118
    10^2: 1178
    10^3: 11680
    10^4: 115730
    10^5: 1146628
    10^6: 11360558
    10^7: 112558960
    

提交回复
热议问题