leetcode| Count Numbers with Unique Digits

Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.

Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99])

题目:意思很明确,找出0 到10的n次方内没有重复数字构成的元素个数,11,101这类都不合格

思路:这道题目十分具有代表性,暴力解法当然是可以的,但是根据题目标签的提示,可以用backtrack和dynamic programming,这个两个算法很值得深入学习一番,看到许多书籍提到过

1,首先是backtrack,学名回溯算法,基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

下面解法的思路是,对给定的数的范围0 ≤ x < 10^n,我们先区间内求1打头的,接着求2打头的.....到9打头的符合规则的数的数量,1打头里面

我们分别求11,12,13....19打头的,如此递归,设置一个boolean数组管理哪个数字为应该规避的,应该规避的数字设为true

public int countNumbersWithUniqueDigits(int n) {
  if (n > 10) {
    return countNumbersWithUniqueDigits(10);
  }
  int count = 1; // x == 0
  long max = (long) Math.pow(10, n);

  boolean[] used = new boolean[10];

  for (int i = 1; i < 10; i++) {
    used[i] = true;
    count += search(i, max, used);
    used[i] = false;
  }

  return count;
}

private static int search(long prev, long max, boolean[] used) {//查询prev打头的,范围为prev-max的个数
  int count = 0;//初始
  if (prev < max) {//递归出口
    count += 1;
  } else {
    return count;
}

/**
* 这段逻辑的意思是,假设prev = 1,i=2,这时used数组中used[1]为true,used[2]为true,curr =12,再接着去查询12打头,到max为止的个数
*/
for (int i = 0; i < 10; i++) {
  if (!used[i]) {
    used[i] = true;
    long cur = 10 * prev + i;
    count += search(cur, max, used);
    used[i] = false;
  }
}

  return count;
}

2,这种思路相对而言简单好理解一点,原理是把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法

本题中我们可以发现,这样一个规律,我们用f(n)标示n位数的符合规则元素的个数,如f(2)标识10-99区间的数目

会发现f(n)符合这样的一个规律,f(n) = 9*9*8...(11-n),于是有如下代码

public int countNumbersWithUniqueDigits(int n) {
  if(n==0) return 1;
    int count = 10;
    int digit = 9;
  for(int i = 2;i<=n;i++){
    digit *= (11-i);
    count+=digit;
  }
  return count;
}

StayHungry 求知若渴 StayFoolish 放低姿态
原文地址:https://www.cnblogs.com/wujunjie/p/5687677.html