// ---------- begin Rerooting ----------
struct Rerooting<Edge, Value, Func, Init> {
    size: usize,
    edge: Vec<(usize, usize, Edge, Edge)>,
    init: Init,
    merge: Func,
    phantom: std::marker::PhantomData<Value>,
}

impl<Edge, Value, Func, Init> Rerooting<Edge, Value, Func, Init>
where
    Edge: Clone,
    Value: Clone,
    Func: Fn(&Value, &Value, &Edge) -> Value,
    Init: Fn(usize) -> Value,
{
    fn new(size: usize, init: Init, func: Func) -> Self {
        Rerooting {
            size: size,
            edge: vec![],
            init: init,
            merge: func,
            phantom: std::marker::PhantomData,
        }
    }
    #[allow(dead_code)]
    fn add_edge(&mut self, a: usize, b: usize, cost: Edge) {
        assert!(a < self.size && b < self.size && a != b);
        self.edge.push((a, b, cost.clone(), cost));
    }
    #[allow(dead_code)]
    fn add_edge_bi(&mut self, a: usize, b: usize, x: Edge, y: Edge) {
        assert!(a < self.size && b < self.size && a != b);
        self.edge.push((a, b, x, y));
    }
    fn solve(&self) -> Vec<Value> {
        let mut graph = vec![vec![]; self.size];
        for e in self.edge.iter() {
            graph[e.0].push((e.1, e.2.clone()));
            graph[e.1].push((e.0, e.3.clone()));
        }
        let root = 0;
        let mut topo = Vec::with_capacity(self.size);
        let mut parent = vec![root; self.size];
        let mut parent_edge: Vec<Option<Edge>> = (0..self.size).map(|_| None).collect();
        let mut stack = vec![root];
        while let Some(v) = stack.pop() {
            topo.push(v);
            if let Some(k) = graph[v].iter().position(|e| e.0 == parent[v]) {
                let x = graph[v].remove(k);
                parent_edge[v] = Some(x.1);
            }
            for e in graph[v].iter() {
                parent[e.0] = v;
                stack.push(e.0);
            }
        }
        assert!(topo.len() == self.size);
        let mut down: Vec<Value> = (0..self.size).map(|k| (self.init)(k)).collect();
        for &v in topo.iter().rev() {
            for e in graph[v].iter() {
                down[v] = (self.merge)(&down[v], &down[e.0], &e.1);
            }
        }
        let mut up: Vec<Value> = (0..self.size).map(|k| (self.init)(k)).collect();
        let mut ans = up.clone();
        for &v in topo.iter() {
            if let Some(e) = parent_edge[v].take() {
                up[v] = (self.merge)(&ans[v], &up[v], &e);
            }
            ans[v] = up[v].clone();
            for e in graph[v].iter() {
                ans[v] = (self.merge)(&ans[v], &down[e.0], &e.1);
            }
            if graph[v].is_empty() {
                continue;
            }
            let mut stack = vec![(graph[v].as_slice(), up[v].clone())];
            while let Some((g, val)) = stack.pop() {
                if g.len() == 1 {
                    up[g[0].0] = val;
                } else {
                    let m = g.len() / 2;
                    let (a, b) = g.split_at(m);
                    let mut p = val.clone();
                    for e in a.iter() {
                        p = (self.merge)(&p, &down[e.0], &e.1);
                    }
                    let mut q = val;
                    for e in b.iter() {
                        q = (self.merge)(&q, &down[e.0], &e.1);
                    }
                    stack.push((b, p));
                    stack.push((a, q));
                }
            }
        }
        ans
    }
}
// ---------- end Rerooting ----------

use std::io::Read;
use std::cmp::min;

// 根から動き始めて
// 0. 根の色が白で全ての頂点を黒にする最小値
// 1. 根の色が黒で全ての頂点を黒にする最小値
// 2. 根の色が白で全ての頂点を黒にして根に戻る最小値
// 3. 根の色が黒で全ての頂点を黒にして根に戻る最小値
// 4. 根の色が白で全ての頂点を黒にして根は白で根に戻る最小値
// 5. 根の色が黒で全ての頂点を黒にして根は白で根に戻る最小値

fn run() {
    let mut s = String::new();
    std::io::stdin().read_to_string(&mut s).unwrap();
    let mut it = s.trim().split_whitespace();
    let n: usize = it.next().unwrap().parse().unwrap();
    let e: Vec<(usize, usize)> = (1..n).map(|_| {
        let x: usize = it.next().unwrap().parse().unwrap();
        let y: usize = it.next().unwrap().parse().unwrap();
        (x - 1, y - 1)
    }).collect();
    let s: Vec<char> = it.next().unwrap().chars().collect();
    type Value = (u32, u32, u32, u32, u32, u32);
    type Edge = usize;
    let init = |_v: usize| -> Value {
        (1, 0, 1, 0, 0, 1)
    };
    let inf = 1_000_000;
    let merge = |a: &Value, b: &Value, c: &Edge| -> Value {
        if s[*c] == 'B' && b.1 == 0 {
            return *a;
        }
        let mut res = (inf, inf, inf, inf, inf, inf);
        if s[*c] == 'B' {
            res.0 = min(a.2 + 1 + b.0, 1 + b.2 + 1 + a.1);
            res.1 = min(a.3 + 1 + b.0, 1 + b.2 + 1 + a.0);
            res.2 = min(a.4 + 1 + b.2 + 1, 1 + b.2 + 1 + a.3);
            res.3 = min(a.5 + 1 + b.2 + 1, 1 + b.2 + 1 + a.2);
            res.4 = min(a.2 + 1 + b.2 + 1, 1 + b.2 + 1 + a.5);
            res.5 = min(a.3 + 1 + b.2 + 1, 1 + b.2 + 1 + a.4);
        } else {
            res.0 = min(a.2 + 1 + b.1, 1 + b.3 + 1 + a.1);
            res.1 = min(a.3 + 1 + b.1, 1 + b.3 + 1 + a.0);
            res.2 = min(a.4 + 1 + b.3 + 1, 1 + b.3 + 1 + a.3);
            res.3 = min(a.5 + 1 + b.3 + 1, 1 + b.3 + 1 + a.2);
            res.4 = min(a.2 + 1 + b.3 + 1, 1 + b.3 + 1 + a.5);
            res.5 = min(a.3 + 1 + b.3 + 1, 1 + b.3 + 1 + a.4);
        }
        res.0 = min(res.0, res.2);
        res.1 = min(res.1, res.3);
        res
    };
    let mut solver = Rerooting::new(n, init, merge);
    for (x, y) in e {
        solver.add_edge_bi(x, y, y, x);
    }
    let dp = solver.solve();
    let mut ans = inf;
    for i in 0..n {
        if s[i] == 'B' {
            ans = min(ans, dp[i].1);
        } else {
            ans = min(ans, dp[i].0);
        }
    }
    println!("{}", ans);
}

fn main() {
    run();
}


Measured memory_bytes: 39096