Sorting Books (Codeforces 1481E)
Tags: Greedy, data structures

One day you wanted to read something, so you went to your bookshelf to grab some book. But when you saw how messy the bookshelf was you decided to clean it up first.  There are $$$n$$$ books standing in a row on the shelf, the $$$i$$$-th book has color $$$a_i$$$.You'd like to rearrange the books to make the shelf look beautiful. The shelf is considered beautiful if all books of the same color are next to each other.In one operation you can take one book from any position on the shelf and move it to the right end of the shelf.What is the minimum number of operations you need to make the shelf beautiful?

Input: The first line contains one integer $$$n$$$ ($$$1 \le n \le 5 \cdot 10^5$$$) — the number of books.The second line contains $$$n$$$ integers $$$a_1, a_2, \dots, a_n$$$ ($$$1 \le a_i \le n$$$) — the book colors.

Output: Output the minimum number of operations to make the shelf beautiful.

Note: In the first example, we have the bookshelf $$$[1, 2, 2, 1, 3]$$$ and can, for example:   take a book on position $$$4$$$ and move to the right end: we'll get $$$[1, 2, 2, 3, 1]$$$;  take a book on position $$$1$$$ and move to the right end: we'll get $$$[2, 2, 3, 1, 1]$$$. In the second example, we can move the first book to the end of the bookshelf and get $$$[2,2,1,1,1]$$$.

Sample Inputs:
5
1 2 2 1 3

5
1 2 2 1 1

Sample Outputs:
2

1

Solution (Python):
class Books:

    def __init__(self, NumberOfBooks, ListOfBooks):
        self.NumberOfBooks = NumberOfBooks
        self.ListOfBooks = [0] + ListOfBooks
        self.First = [0] * (self.NumberOfBooks + 1)
        self.Last = [0] * (self.NumberOfBooks + 1)

    def Find_First_Last(self):
        for i in range(1, self.NumberOfBooks + 1):
            if not self.First[self.ListOfBooks[i]]:
                self.First[self.ListOfBooks[i]] = i
            self.Last[self.ListOfBooks[i]] = i

class Sorting_Books(Books):

    def __init__(self, NumberOfBooks, ListOfBooks):
        Books.__init__(self, NumberOfBooks, ListOfBooks)
        self.ColorCounter = [0] * (self.NumberOfBooks + 1)
        self.dp = [0] * (self.NumberOfBooks + 2)

    def Solve(self):
        self.Find_First_Last()
        for i in range(self.NumberOfBooks, 0, -1):
            self.ColorCounter[self.ListOfBooks[i]] += 1
            if i == self.First[self.ListOfBooks[i]]:
                self.dp[i] = max(self.dp[i + 1], self.dp[self.Last[self.ListOfBooks[i]] + 1] + self.ColorCounter[self.ListOfBooks[i]])
            else:
                self.dp[i] = max(self.dp[i + 1], self.ColorCounter[self.ListOfBooks[i]])
        return self.NumberOfBooks - self.dp[1]
if __name__ == '__main__':
    books_obj = Sorting_Books(int(input()), list(map(int, input().split())))
    print(books_obj.Solve())

Solution (Python):
n = int(input())
a = list(map(int, input().split()))
a.insert(0, None)
left = [0] * (n + 1)
right = [0] * (n + 1)
sum_val = [0] * (n + 1)
for i in range(n, 0, -1):
    if right[a[i]] == 0:
        right[a[i]] = i
        sum_val[i] = 1
    else:
        sum_val[i] = sum_val[left[a[i]]] + 1
    left[a[i]] = i
dp = [0] * (n + 2)
for i in range(n, 0, -1):
    if left[a[i]] == i:
        dp[i] = max(dp[i + 1], sum_val[i] + dp[right[a[i]] + 1])
    else:
        dp[i] = max(dp[i + 1], sum_val[i])
''
print(n - dp[1])

Solution (Python):
class Sorting_Books:

    def __init__(self, NumberOfBooks, ListOfBooks):
        self.NumberOfBooks = NumberOfBooks
        self.ListOfBooks = [0] + ListOfBooks
        self.First = [0] * (self.NumberOfBooks + 1)
        self.Last = [0] * (self.NumberOfBooks + 1)
        self.ColorCounter = [0] * (self.NumberOfBooks + 1)
        self.dp = [0] * (self.NumberOfBooks + 2)

    def Find_First_Last(self):
        for i in range(1, self.NumberOfBooks + 1):
            if not self.First[self.ListOfBooks[i]]:
                self.First[self.ListOfBooks[i]] = i
            self.Last[self.ListOfBooks[i]] = i

    def Solve(self):
        self.Find_First_Last()
        for i in range(self.NumberOfBooks, 0, -1):
            self.ColorCounter[self.ListOfBooks[i]] += 1
            self.dp[i] = self.dp[i + 1]
            if i == self.First[self.ListOfBooks[i]]:
                self.dp[i] = max(self.dp[i], self.dp[self.Last[self.ListOfBooks[i]] + 1] + self.ColorCounter[self.ListOfBooks[i]])
            else:
                self.dp[i] = max(self.dp[i], self.ColorCounter[self.ListOfBooks[i]])
        return self.NumberOfBooks - self.dp[1]
if __name__ == '__main__':
    books_obj = Sorting_Books(int(input()), list(map(int, input().split())))
    print(books_obj.Solve())