// https://atcoder.jp/contests/tenka1-2019-beginner/tasks/tenka1_2019_c
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define REP(i, n) FOR(i, 0, n)
#define FOR(i, s, n) for (int i = (s), i##_len = (n); i < i##_len; ++i)
#define ALL(obj) (obj).begin(), (obj).end()
#define ALLR(obj) (obj).rbegin(), (obj).rend()
#define CEIL(a, b) ((a - 1) / b + 1)
/**
 * @brief Segment-Tree(セグメント木)
 * @docs docs/segment-tree.md
 */
template <typename Monoid, typename F> struct SegmentTree {
    int sz;
    vector<Monoid> seg;

    const F f;
    const Monoid M1;

    // サイズnのセグ木の初期化。fは2つの要素をマージする二項演算, M1は単位元
    SegmentTree(int n, const F f, const Monoid &M1) : f(f), M1(M1) {
        sz = 1;
        while (sz < n)
            sz <<= 1;
        seg.assign(2 * sz, M1);
    }

    // k番目の要素にxをセットする。後でbuildが必要
    void set(int k, const Monoid &x) { seg[k + sz] = x; }

    // setした結果で構築する
    void build() {
        for (int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    void update(int k, const Monoid &x) {
        k += sz;
        seg[k] = x;
        while (k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    // 区間 [a,b)に対して二項演算した結果を返す
    Monoid query(int a, int b) {
        Monoid L = M1, R = M1;
        for (a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if (a & 1)
                L = f(L, seg[a++]);
            if (b & 1)
                R = f(seg[--b], R);
        }
        return f(L, R);
    }

    // k 番目の要素を返す
    Monoid operator[](const int &k) const { return seg[k + sz]; }

    template <typename C>
    int find_subtree(int a, const C &check, Monoid &M, bool type) {
        while (a < sz) {
            Monoid nxt =
                type ? f(seg[2 * a + type], M) : f(M, seg[2 * a + type]);
            if (check(nxt))
                a = 2 * a + type;
            else
                M = nxt, a = 2 * a + 1 - type;
        }
        return a - sz;
    }

    // [a,x)が check を満たす最初の要素位置xを返す
    template <typename C> int find_first(int a, const C &check) {
        Monoid L = M1;
        if (a <= 0) {
            if (check(f(L, seg[1])))
                return find_subtree(1, check, L, false);
            return -1;
        }
        int b = sz;
        for (a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if (a & 1) {
                Monoid nxt = f(L, seg[a]);
                if (check(nxt))
                    return find_subtree(a, check, L, false);
                L = nxt;
                ++a;
            }
        }
        return -1;
    }

    // [x,b) が check を満たす最後の要素位置xを返す
    template <typename C> int find_last(int b, const C &check) {
        Monoid R = M1;
        if (b >= sz) {
            if (check(f(seg[1], R)))
                return find_subtree(1, check, R, true);
            return -1;
        }
        int a = sz;
        for (b += sz; a < b; a >>= 1, b >>= 1) {
            if (b & 1) {
                Monoid nxt = f(seg[--b], R);
                if (check(nxt))
                    return find_subtree(b, check, R, true);
                R = nxt;
            }
        }
        return -1;
    }
};

template <typename Monoid, typename F>
SegmentTree<Monoid, F> get_segment_tree(int N, const F &f, const Monoid &M1) {
    return {N, f, M1};
}

void solve() {
    int N;
    cin >> N;
    string S;
    cin >> S;
    // 区間sum
    auto seg = get_segment_tree(
        N, [](int a, int b) { return a + b; }, 0);
    REP(i, N) {
        if (S[i] == '#') {
            seg.set(i, 1);
        }
    }
    seg.build();
    // ある地点を境に....####にする
    int ans = 1e15;
    REP(i, N + 1) {
        int left = seg.query(0, i);
        int right = seg.query(i, N + 1);
        ans = min(ans, left + (N - i) - right);
    }
    cout << ans << endl;
}

signed main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}


Measured memory_bytes: 5712