你怎么知道一个阵列在另一个阵列中重复多少次?

时间:2015-09-22 23:21:49

标签: java arrays

例如,如果您将{1,2}作为小数组而{1,2,3,4,1,2,1,3}作为大数组,那么它将返回2.

这可能非常不正确:

public static int timesOccur(int[] small, int big[]) {
    int sum= 0; 
    for (int i=0; i<small.length; i++){
        int currentSum = 0;
        for (int j=0; j<big.length; j++){       
            if (small[i] == big[j]){
                currentSum ++;
            }   
            sum= currentSum ;
        }
    }
    return sum;
}

4 个答案:

答案 0 :(得分:4)

正如@AndyTurner所提到的,您的任务可以简化为一组众所周知的字符串匹配算法。

据我所知,你想要的解决方案比O(n * m)更快。

主要有两种方法。首先涉及预处理文本(长数组),第二涉及预处理搜索模式(小数组)。

  1. 预处理文字。我的意思是从较长的数组中创建suffix arrayLCP。构建此数据结构后,您可以执行二进制搜索以查找您的子字符串。您可以实现的最有效时间是O(n)构建LCP和O(m + log n)来执行搜索。所以整体时间是O(n + m)。

  2. 预处理模式。这意味着从模式构建DFA。构造DFA后,它需要遍历一个字符串(长数组)来查找所有出现的子字符串(线性时间)。这里最难的部分是构建DFA。 Knuth-Morris-Pratt在O(m)时间内执行此操作,因此整体算法运行时间将为O(m + n)。实际上,就效率和实现复杂性而言,KMP算法很可能是此任务的最佳可用解决方案。请查看@JuanLopes的答案,了解具体实施情况。

  3. 此外,您可以考虑优化的强力,例如Boyer-Moore,它适用于实际情况,但在最坏的情况下运行时间为O(n * m)。

    UPD:

    如果您不需要快速接近,我会从描述中更正您的代码:

        public static int timesOccur(int[] small, int big[]) {
            int sum = 0;
            for (int i = 0; i < big.length - small.length + 1; i++) {
                int j = 0;
                while (j < small.length && small[j] == big[i + j]) {
                    j++;
                }
                if (j == small.length) {
                    sum++;
                }
            }
            return sum;
        }
    

    注意内在的while循环。一旦元素不匹配就会停止。这是一项重要的优化,因为它使运行时间几乎呈线性,适用于最佳情况。

    upd2:内循环解释。

    内循环的目的是找出较小的数组是否匹配从位置i开始的较大数组。为了执行该检查,索引j从0迭代到较小数组的长度,将较小数组的元素j与较大数组的对应元素i + j进行比较。当两个条件同时为真时循环继续:j < small.length 两个数组的相应元素匹配。

    所以循环在两种情况下停止:

    1. j < small.length false 。这意味着j==small.length。此外,它意味着对于匹配的两个数组的所有j=0..small.length-1元素(否则循环会先破坏,请参阅下面的(2))。
    2. small[j] == big[i + j] false 。这意味着找不到匹配项。在这种情况下,循环将在j到达small.length
    3. 之前中断

      在循环之后,它足以检查j==small.length是否知道哪个条件循环停止,因此知道当前位置i是否找到匹配。

答案 1 :(得分:3)

这是一个简单的子阵列匹配问题。在Java中,您可以使用Collections.indexOfSublist,但是您必须将数组中的所有整数都包装起来。一个选项是实现自己的数组匹配算法。有几个选项,大多数string searching algorithms可以适应这项任务。

以下是基于KMP algorithm的优化版本。在最坏的情况下,它将是O(n + m),这比普通算法更好。但它有一个缺点,需要额外的空间来计算失败函数(F)。

public class Main {
    public static class KMP {
        private final int F[];
        private final int[] needle;

        public KMP(int[] needle) {
            this.needle = needle;
            this.F = new int[needle.length + 1];

            F[0] = 0;
            F[1] = 0;
            int i = 1, j = 0;
            while (i < needle.length) {
                if (needle[i] == needle[j])
                    F[++i] = ++j;
                else if (j == 0)
                    F[++i] = 0;
                else
                    j = F[j];
            }
        }

        public int countAt(int[] haystack) {
            int count = 0;
            int i = 0, j = 0;
            int n = haystack.length, m = needle.length;

            while (i - j <= n - m) {
                while (j < m) {
                    if (needle[j] == haystack[i]) {
                        i++;
                        j++;
                    } else break;
                }
                if (j == m) count++;
                else if (j == 0) i++;
                j = F[j];
            }
            return count;
        }
    }

    public static void main(String[] args) {
        System.out.println(new KMP(new int[]{1, 2}).countAt(new int[]{1, 2, 3, 4, 1, 2, 1, 3}));
        System.out.println(new KMP(new int[]{1, 1}).countAt(new int[]{1, 1, 1}));
    }
}

答案 2 :(得分:0)

不是发布解决方案,而是提供一些提示让你感动。

值得将问题分解成更小的部分,通常你的算法应该是这样的:

for each position in the big array
     check if the small array matches that position
         if it does, increment your counter

然后较小的部分检查小阵列是否与给定位置匹配

first check if there's enough room to fit the smaller array
    if not then the arrays don't match
otherwise for each position in the smaller array
    check if the values in the arrays match
        if not then the arrays don't match
if you get to the end of the smaller array and they have all matched
    then the arrays match

答案 3 :(得分:0)

虽然未经过彻底测试,但我相信这是解决您问题的方法。我强烈建议使用Sprinters伪代码在使用之前尝试自己解决这个问题。

public static void main(String[] args)
{
    int[] smallArray = {1,1};
    int[] bigArray = {1,1,1};
    int sum = 0;

    for(int i = 0; i < bigArray.length; i++)
    {
        boolean flag = true;
        if(bigArray[i] == smallArray[0])
        {
            for(int x = 0; x < smallArray.length; x++)
            {
                if(i + x >= bigArray.length)
                    flag = false;
                else if(bigArray[i + x] != smallArray[x])
                    flag = false;

            }

            if(flag)
                sum += 1;
        }
    }

    System.out.println(sum);

}

}