There is a simple iterative dynamic-programming algorithm to solve this: for all i from 1 to n
(length of the permutation) take the number i
and look how many elements in P
left of i
have already been seen. Since we process i
in increasing order, we know that the elements not seen are the elements bigger than i
- and so we count and write down the number of those elements. The trick is to introduce an external list than keeps track of which elements in P
have already been seen.
For the start, lets look at how to do it in O(n^2)
way. For example, if P=[4, 3, 2, 1]
, then the algorithm would executed as follows:
Create a structure tree
initialized to zeros. It holds "1" in position j
if element with j-th position in P
has already been seen by the iterative algorithm.
Take 1, determine that pos==3
. Write "1" down in the tree[pos]
. Calculate num_seen=sum(tree[0:3])
which is equal to 0. Write down pos - num_seen + 1
at I[0]
. After this: tree = [0, 0, 0, 1], I = [3, 0, 0, 0]
Take 2, write "1" down in the tree[1] and 1 at I[1]. tree = [0, 1, 0, 1], I=[3,1,0,0]
.
Take 3, write "1" down in the tree[2] and 0 at I[2]. tree = [0, 1, 1, 1], I=[3,1,0,0]
.
Take 4, write "1" down in the tree[0] and 0 at I[3]. tree = [1, 1, 1, 1], I=[3,1,0,0]
.
The second trick is to use an efficient data structure to count the number of the seen elements in O(n log n)
time instead of O(n^2)
as above.
Here is Python code that uses a Fenwick tree to count the number of seen elements in a fast way:
def ft_sum(tree, a, b):
if a == 0:
s = 0;
while b >= 0:
s += tree[b];
b = (b & (b + 1)) - 1
return s
return ft_sum(tree, 0, b) - ft_sum(tree, 0, a - 1)
def ft_adjust(tree, k, v):
while k < len(tree):
tree[k] += v
k |= k + 1
def calcI(P):
n = len(P)
tree = [0] * n
I = [0] * n
positions = [0] * n
for i in xrange(n):
positions[P[i]-1] = i
tree = [0] * n
for i in xrange(n):
pos = positions[i]
ft_adjust(tree, pos, 1)
num_seen = ft_sum(tree, 0, pos)
I[i] = pos - num_seen + 1
return I