We equivalently view the cyclically shifting as a pointer starting at $j$ that moves left and right cyclically.
Let's use 0-indexing that wraps around modulo $N$ (so $A_i=A_{i\pmod N}$).
Linear Time Solution 1:
Claim. For a given starting index $j$, the optimal pointer route consists of at most one turn (LR or RL).
Proof. The indices visited from $j$ form a contiguous segment including $j$. Whatever this interval is, it can be traversed by going all the way to one end, turning, and going to the other end. This is optimal because the indices $j$, $l$, and $r$ must all be visited at some point, either in the order $j \rightarrow l \rightarrow r$ or $j \rightarrow r \rightarrow l$. This requires at least $\min(|j-l|+|r-l|, |j-r|+|l-r|)$ movements. (done proof)
Without loss of generality, say the optimal route from $j$ starts with L. Then the path must look like $L...L R ...R$. Or, equivalently, visiting an interval $[l, r]$ by going to $l\le j$ first then right to $r$. This costs $(j-l)+(r-l) = j+(r-2l)$. It suffices to consider right-minimal $[l, r]$ -- $r$ must the smallest integer $\ge l$ such that $[l, r]$ covers all of $S$. We can sweep $j$ from $0$ to $N-1$ and
Incrementing $j$ incurs an $O(1)$ time to do the first and amortized linear time to do the second.
We account for the start moving right case analogously.
Claire's Python Code:
n = int(input())
a = list(map(int, input().split()))
a = [x-1 for x in a]
A = a + a
ans = [2*n] * (2*n)
def solve(arr, _ans):
m = len(arr)
need = len(set(a))
cnt = [0] * n
r = -1
best = 10**18
for j in range(m):
while r + 1 < m and need > 0:
r += 1
cnt[arr[r]] += 1
if cnt[arr[r]] == 1:
need -= 1
if need == 0:
best = min(best, r - 2*j)
_ans[j] = min(_ans[j], j + best)
cnt[arr[j]] -= 1
if cnt[arr[j]] == 0:
need += 1
for _ in range(2):
solve(A, ans)
A.reverse()
ans.reverse()
for i in range(n):
print(min(ans[i], ans[n + i]), end=' ')
Linear Time Solution 2:
We note that the optimal route can always be split into two parts:
Initialize $ans_i=\infty$ for all $i$ from $0$ to $N-1$. We can handle the two parts as follows:
Note: We compute right-minimal intervals $[l,r]$ in a different way from Solution 1. Specifically, we first
Ben's C++ code:
#include <bits/stdc++.h>
using namespace std;
template <class T> using V = vector<T>;
#define all(x) begin(x), end(x)
void ckmin(int &a, int b) { a = min(a, b); }
void ckmax(int &a, int b) { a = max(a, b); }
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
V<int> A(N);
for (int &t : A) cin >> t;
V<int> lst(N + 1, -1), nxt(N, -1);
V<int> ans(N, N);
auto add_ival = [&](int l, int r) {
assert(l <= r);
int len = r - l;
ckmin(ans.at(l), len);
ckmin(ans.at(r % N), len);
};
int r = 0;
for (int i = 0; i < 2 * N; ++i) {
int &ls = lst.at(A.at(i % N));
if (ls == -1) {
ckmax(r, i);
} else if (ls < N) nxt.at(ls) = i;
ls = i;
}
for (int i = 0; i < N; ++i) {
add_ival(i, r);
assert(nxt.at(i) != -1);
ckmax(r, nxt.at(i));
}
for (int it = 0; it < 2; ++it) {
for (int i = 0; i < N; ++i) ckmin(ans.at((i + 1) % N), ans.at(i) + 1);
for (int i = N - 1; i >= 0; --i)
ckmin(ans.at(i), ans.at((i + 1) % N) + 1);
}
for (int i = 0; i < N; ++i) {
if (i) cout << " ";
cout << ans[i];
}
cout << "\n";
}