周赛273——[5965. 相同元素的间隔之和 ]

536 阅读3分钟

比赛前10几分钟才想出来思路,没来得及做。记录一下

首先,不难看出应该要把相同数字的下标一起记下来。这里用一个map记录就行,key是数字,value是一个list,list的内容是数字的下标。

如果用暴力法来解,复杂度是O(n^2),对于10^5次方的输入规模会超时。

比赛时候主要猜测了几个方向:

1.由于下标序列list是个递增的数组,会不会是用二分??

2.dp??

3.找规律

尝试了一轮思考之后还是觉得通过找规律比较靠谱,还有一点是通过前一个数字的和直接推导出下一个,这样才可以避免O(n^2)的复杂度。

最后在草稿纸上画了下才明白,思路如下:

比如对于某个序列:[1,2,5,7,8,11]。

对于2来说,它的绝对值之和是:1 + 3 + 5 + 6 + 9 = 24

image-20211226133102607.png

对于5,它的绝对值之和是:4 + 3 + 2 + 3 + 6 = 18

image-20211226133203587.png

5的绝对值之和和2的绝对值之和之间的变化,我们可以分开两部分看。

对于5的右边的3个数:7,8,11。这3个数到2的绝对值都减少了3. 这个3是怎么来的呢? 就是5和2之间的差值。

所以一共少了3 * 3 = 9

对称地,对于左边来说,本来最左边的1只需要计算到2之间的距离绝对值(就是1),但是现在1需要多了2到5这一段的距离(3)。所以左边距离之和增加了1 * 3。

所以,2的绝对值之和到5的绝对值之和的变化是:

1.右边减少了3 * 3

2.左边增加了1 * 3.

因为2的绝对值之和是24,因此5的绝对值之和就是24 + (1 * 3) - (3 * 3) = 24 - 6 = 18

因此算法的思路就是:

分别求出左边增加的距离减去右边减少的距离。增加的个数和减少的个数分别是:i - 1 和list.size() - i - 1

代码如下:

public long[] getDistances(int[] arr) {
    Map<Integer, List<Integer>> map = new HashMap<>();
    for (int i = 0; i < arr.length; i++) {
        List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
        list.add(i);
        map.put(arr[i], list);
    }

    long[] result = new long[arr.length];
    for (List<Integer> l : map.values()) {
        solve(l, result);
    }
    return result;
}

private void solve(List<Integer> l, long[] result) {
    long last = -1L;
    for (int i = 0; i < l.size(); i++) {
        if (i == 0) {
            long tmpAbsSum = 0L;
            for (int j = 1; j < l.size(); j++) {
                tmpAbsSum += (Math.abs(l.get(0) - l.get(j)));
            }
            result[l.get(0)] = tmpAbsSum;
            last = tmpAbsSum;
            continue;
        }
        // 前后之差
        long difference = Math.abs(l.get(i) - l.get(i - 1));
        // 前面增加的
        long add = i - 1;

        //后面减少的
        long minus = (l.size() - i - 1);

        long current = last + (add - minus) * difference;
        result[l.get(i)] = current;
        last = current;
    }
}
func getDistances(arr []int) []int64 {
	pos := map[int][]int{}
	for i, v := range arr {
		pos[v] = append(pos[v], i) // 记录相同元素的位置
	}
	ans := make([]int64, len(arr))
	for _, p := range pos {
		sum := 0
		for _, i := range p {
			sum += i - p[0]
		}
		ans[p[0]] = int64(sum) // 最左侧元素的间隔和
		for i, n := 1, len(p); i < n; i++ { // 计算下一个相同元素的间隔和
			sum -= (n - i*2) * (p[i] - p[i-1]) // 到右边的 n-i 个点的距离更近了,同时到左边 i 个点的距离更远了
			ans[p[i]] = int64(sum)
		}
	}
	return ans
}