【问题标题】:Count the number of all possible distinct 3-digit numbers given a String array计算给定字符串数组的所有可能的不同 3 位数字的数量
【发布时间】:2020-07-28 22:13:11
【问题描述】:

下面是我的代码,用于计算可以正常工作的不同 3 位字符串的数量,但是,我想优化此代码以提高时间复杂度。有人可以帮助我吗?

输入:[1,2,1,4]

输出:12

谢谢。

static int countUnique(String [] arr)
{
    Set<String> s = new TreeSet<>();
    for (int i = 0; i<arr.length; i++)
    {
        for (int j = 0; j<arr.length; j++)
        {
            for (int k = 0; k<arr.length; k++)
            {
                if (i!=j && j != k && i!=k)
                    s.add((arr[i] +""+arr[j]+""+arr[k]));
            }
        }
    }
    return s.size();
}

【问题讨论】:

  • 您需要考虑算法,而不是使用蛮力。您可能想从这里开始:baeldung.com/java-combinatorial-algorithms 或谷歌“java 组合学教程”以获取其他来源。
  • 我会说你的问题看起来像纯数学。提示:组合数学的基础知识就足够了。
  • @Ecto Basic 组合实际上不足以解决这个问题,即使它适用于一些看起来相似的问题。
  • 为什么你的输入是字符串?您是否需要担心由多个不同的输入字符串集产生相同的输出字符串的情况(例如,您的输入是[1, 12, 23, 3, 4],您可以从12 + 3 + 产生1234 41 + 23 + 4)?或者您只是使用字符串和连接作为一种简单的方法来为Set 生成可比较的值?
  • @KarlKnechtel OP 表示他们的代码工作正常。因此,输入的元素是单个数字,如 OP 的正确代码所示。任务限制为 3 位长度。

标签: java algorithm count time-complexity unique


【解决方案1】:

这是O(n) 解决方案:

依次迭代每个不同的可用数字。

(A) 如果有三个实例,则加1,占该数字的三个组成的一个字符串。

(B) 如果它有两个实例,则将已迭代的位数相加 3 倍,占 3 选择 2 种方式将该数字的两个实例与另一个已迭代的数字排列。

(C) 加上我们可以选择到目前为止看到的两个数字的方式的数量,考虑到每个数字只安排一个这个数字的实例。

(D) 最后,将两位数排列方式的计数添加到我们的记录中:如果该位有两个实例,则添加 3 选择 2 = 3,即仅排列该位的两个实例。还要加上 (2 * 3 choose 2 = 6) 乘以已经迭代过的位数,说明将这个数字与另一个已经看到的数字排列方式的数量。

例如:

1 2 1 4

1 -> D applies, add 3 to the two-digit-arrangements count
  11x, 1x1, x11

2 -> C applies, add 3 to result
  112, 121, 211
     D applies, add 6 to the two-digit-arrangements count (total 9)
  12x, 1x2, x12, 21x, 2x1, x21
  
4 -> C applies, add 9 to result

Result 12

带有随机测试的 JavaScript 代码,与您的蛮力方法比较:

function f(A){
  const counts = {};
  
  for (let a of A)
    counts[a] = counts[a] ? -~counts[a] : 1;

  let result = 0;
  let numTwoDigitArrangements = 0;
  let numSeen = 0;
  
  for (let d of Object.keys(counts)){
    if (counts[d] > 2)
      result += 1;

    if (counts[d] > 1)
      result += 3 * numSeen;

    result += numTwoDigitArrangements;

    if (counts[d] > 1)
      numTwoDigitArrangements += 3;
        
    numTwoDigitArrangements += 6 * numSeen;

    numSeen = numSeen + 1;
  }
  
  return result;
}


function bruteForce(arr){
  const s = new Set();
    
  for (let i=0; i<arr.length; i++){
    for (let j=0; j<arr.length; j++){
      for (let k=0; k<arr.length; k++){
        if (i != j && j != k && i != k)
          s.add((arr[i] + "" + arr[j]+ "" + arr[k]));
      }
    }
  }
  
  return s.size;
}


// Random tests
var numTests = 500;
var maxLength = 25;

for (let i=0; i<numTests; i++){
  const n = Math.ceil(Math.random() * maxLength);
  const A = new Array(n);
  
  for (let j=0; j<n; j++)
    A[j] = Math.floor(Math.random() * 10);
    
  const _f = f(A);
  const _bruteForce = bruteForce(A);
  
  if (_f != _bruteForce){
    console.log('Mismatch found:');
    console.log('' + A);
    console.log(`f: ${ _f }`);
    console.log(`brute force: ${ _bruteForce }`);
  }
}

console.log('Done testing.');

【讨论】:

    【解决方案2】:

    解决此问题的另一种方法是回溯算法。使用回溯可以解决任何组合或排列类型的问题。 以下是有关回溯算法的一些信息 - https://en.wikipedia.org/wiki/Backtracking

    注意:这不是最优化的解决方案,也不是 O(n) 解决方案。这个解决方案是 O(n! * n)。但是有很多机会可以使其更加优化。

    使用回溯的 Java 代码:

    int countUniqueOpt(String[] arr) {
    
            //Set to avoid duplicates
            Set<String> resultList = new HashSet<>();
    
            backtracking(arr, 3, resultList, new ArrayList<>());
    
            return resultList.size();
        }
    
        void backtracking(String[] arr, int k, Set<String> resultList, List<Integer> indexList) {
    
            if (indexList.size() == k) {
                String tempString = arr[indexList.get(0)] + arr[indexList.get(1)] + arr[indexList.get(2)];
                resultList.add(tempString);
            } else {
    
                for (int i = 0; i < arr.length; i++) {
                    if (!indexList.contains(i)) {
                        indexList.add(i);
    
                        backtracking(arr, k, resultList, indexList);
    
                        indexList.remove(indexList.size() - 1);
                    }
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2013-03-21
      • 2016-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-07
      • 2018-06-29
      • 2018-05-14
      相关资源
      最近更新 更多