查找数字流中的AP三元组数

时间:2013-07-04 12:53:35

标签: java algorithm

问题:

  

给定N个整数A1,A2,.... AN,Dexter想知道他有多少种方法可以选择三个数字,这样他们就是算术级数的三个连续项。

CodeChef link

这是我的解决方案(让“freq”成为计数器)

 1. Create a data store (array of sorted sets) to hold a sorted set of positions of number i in stream at index i in array.
 2. for k: 0 to array.length
    a. get Sorted Set S[k]
    b. if SZ >=3, where SZ = S[k].size, compute SZ choose 3 and add it to freq
    c. for r: 2*k-1 to k
           for x in S[k]
           find entries in S[r], say A, more than x and entries in S[r-i], say B, less than x.. freq += A*B
           find entries in S[r], say A, less than x and entries in S[r-i], say B, more than x.. freq += A*B 

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

/**
 *
 * @author abhishek87
 */
class APTripletInStream {

    public static void main(String[] args) {

        int idx=0, numInStream;
        Scanner scanIn = new Scanner(System.in), readLine;        

        String line = scanIn.nextLine();
        readLine = new Scanner(line);          

        DataStore dStore = new DataStore(30000 + 1);

        while(scanIn.hasNextLine()) {
            line = scanIn.nextLine();
            readLine = new Scanner(line);
            while(readLine.hasNextInt()){
                numInStream = readLine.nextInt();
                dStore.add(++idx, numInStream); 
            }
            break;                       
        }
        Long res = 0L;
        try {
            res = APProblemSolver.solveProblem(dStore);  
        } catch(Exception ex) {
            res = 0L;
        }
        System.out.println(res);        
    }
}

class APProblemSolver {
    public static Long solveProblem(DataStore dStore) {
        Long freq = 0L;
        int dSize = dStore.size();
        for(int idx=1; idx<=dSize-1; idx++) {
            Set currSet = dStore.getSetAtIndex(idx);
            if(null != currSet && !currSet.isEmpty()) {

                int size = currSet.size();
                if(size >= 3) {
                    freq += (size*(long)(size-1)*(long)(size - 2)/6L);
                }

                for(int right = 2*idx-1; right > idx; right--){
                    if(right >= dSize)
                        continue;
                    Set rightSet = dStore.getSetAtIndex(right);
                    Set leftSet = dStore.getSetAtIndex(2*idx - right);
                    if(null != rightSet && null != leftSet) {
                        for(Object obj : currSet) {
                            Set leftSetHeadSet = ((TreeSet)leftSet).headSet(obj);
                            Set rightSetTailSet = ((TreeSet)rightSet).tailSet(obj);
                            freq += leftSetHeadSet.size() * rightSetTailSet.size();

                            Set leftSetTailSet = ((TreeSet)leftSet).tailSet(obj);
                            Set rightSetHeadSet = ((TreeSet)rightSet).headSet(obj);  
                            freq += leftSetTailSet.size() * rightSetHeadSet.size();
                        }
                    }
                }                
            }
        }        
        return freq;
    }           
}

class DataStore {

    private TreeSet[] list = null;
    private int size;

    public DataStore(int size) {
        this.size = size;
        list = new TreeSet[size];
    }    

    public void add(Integer idx, Integer val) {
        Set<Integer> i = list[val];
        if(null == i) {
            i = new TreeSet<Integer>();
            i.add(idx);
            list[val] = (TreeSet<Integer>)i;
        } else{
            ((TreeSet<Integer>)list[val]).add(idx);
        }
    }

    public int size() {
        return size;
    }    

    public Set getSetAtIndex(int idx) {
        return list[idx];
    }
}

以下是我要找的内容:

  1. 当我提交问题时,我得到“超出时间限制”。因此,我想使用NetBeans Profiler来估计此解决方案所需的时间,以便我可以改进它。 仅供参考 - 成功提交的时间限制为3秒

  2. 任何人都可以通过以下方式给我一些指导来改进我的解决方案[我不想改变我的解决方案]:

    • 优化存储
    • 我的解决方案的哪些部分耗时且有明显的解决方法
  3. 示例:

    输入:

    Number Of entries - 10.
    Number Stream - 3 5 3 6 3 4 10 4 5 2.
    

    输出:

    9.
    

    说明:

    The followings are all 9 ways to choose a triplet:
    (Ai, Aj, Ak) = (3, 3, 3)
    (Ai, Aj, Ak) = (3, 4, 5)
    (Ai, Aj, Ak) = (3, 4, 5)
    (Ai, Aj, Ak) = (3, 4, 5)
    (Ai, Aj, Ak) = (3, 4, 5)
    (Ai, Aj, Ak) = (6, 4, 2)
    (Ai, Aj, Ak) = (6, 4, 2)
    (Ai, Aj, Ak) = (3, 4, 5)
    (Ai, Aj, Ak) = (3, 4, 5)
    

3 个答案:

答案 0 :(得分:1)

我没有详细检查你的代码,但这是我将如何做的:

Sort your list -- 1
Iterate through your sorted list (i from 0 to n) -- 2
    Iterate though the remaining part of the list (j from i+1 to n) -- 2.a
        Lookup if (2*j-i) which would be the third element of the arithmetic progression -- 2.a.1

步骤1是O(n * log(n))但是由于二进制搜索,它允许步骤2.a.1为O(log(n-j))。

这是我的python实现:

from bisect import bisect_left

def index_in_sorted(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    return None

numbers=[4,5,6,17,9,1,442,44,32,3,21,19]
print numbers
numbers.sort()

n = len(numbers)
for i in range(0,n):
    n_i = numbers[i]
    for j in range(i+1,n):
        n_j = numbers[j]
        n_k = 2*n_j - n_i
        if index_in_sorted(numbers,n_k): # I could only process the end of numbers but it's not worth the pain
            print "Found", n_i,n_j,n_k

答案 1 :(得分:1)

您应该实施数据存储区的lazy instantiation

public DataStore(int size) {
        for(int i=0; i<size;i++)
            list.add(i, new TreeSet<Integer>());      
    }    

在实例化期间创建30001个树集。 拥有所需内容的地图int -> Set会好得多。然后在代码dStore.getSetAtIndex(right)中,如果没有为此int设置,则实例化它。

明显的部分是:

for(Object objMore : leftSetTailSet) {
      for(Object objLess : rightSetHeadSet) {
          freq++;                                        
      }                                
}    

可以更改为freq += leftSetTailSet*rightSetHeadSet;

此外,我没有看到dsStore大小发生变化:

而不是:{for}循环中的idx<=dStore.size()-1;您可以声明变量dsSize = dStore.size()并拥有idx < dsSizeif(right >= dsSize)

答案 2 :(得分:1)

最重要的是,如果你有前两个学期,那么第三个学期是固定的。

利用记忆力你可以做得更好。

让我们有一个数组数组。我不知道你是如何用Java做的,这里是C ++版本。

vector<vector<int> > where

其中[i] =输入中的位置 value = i

所以{1,4,2,3,3}看起来像

where[0]={}
where[1]={0}
where[2]={2}
where[3]={3,4}
where[4]={1}

如果初始化上面的向量向量,那么将对位置进行排序。

再次,您可以设置AP的前2个元素,现在不是在原始输入流中搜索第三个元素,而是可以在 中轻松查找。

我总是结束算法问题:我们能做得更好吗?我确信有更好的方法,如果我点击它,我会更新这个答案。