using System;
using System.Collections.Generic;
using System.Text;
using KVP = System.Collections.Generic.KeyValuePair<int, int>;
using System.IO;

namespace E.TreeStringProblem
{
    class Program
    {
        static string s;
        static List<int> pfunc;
        static List<string> edges;
        static List<List<int>> children;
        static Queue<KVP> queue;
        static int[] lengths;

        static void Main()
        {

#if FILEIO
            StreamReader r = new StreamReader("../../in");
            StreamWriter w = new StreamWriter("../../out");
#else
            TextReader r = Console.In;
            TextWriter w = Console.Out;
#endif

            int n = int.Parse(r.ReadLine());

            children = new List<List<int>>(n);
            for (int i = 0; i < children.Capacity; i++)
                children.Add(new List<int>());

            pfunc = new List<int>();
            pfunc.Add(0);

            edges = new List<string>(n);
            edges.Add(string.Empty);

            for (int i = 0; i < (n - 1); i++)
            {
                string[] buff = r.ReadLine().Split(' ');
                int p = int.Parse(buff[0]);

                children[p - 1].Add(i + 1);
                edges.Add(buff[1]);
            }
            s = r.ReadLine();

            lengths = new int[n];
            Stack<int> st_lengths = new Stack<int>();
            st_lengths.Push(-1);
            while (st_lengths.Count != 0)
            {
                int x = st_lengths.Pop();
                if (x < 0)
                {
                    x = (-x) - 1;
                    st_lengths.Push(x + 1);
                    foreach (int ch in children[x])
                    {
                        if (children[ch].Count != 0)
                            st_lengths.Push(-(ch + 1));
                    }
                }
                else
                {
                    x--;
                    foreach (int ch in children[x])
                        lengths[x] = Math.Max(lengths[x], lengths[ch] + edges[ch].Length);
                }
            }

            queue = new Queue<KVP>();
            queue.Enqueue(new KVP(0, 0));

            int cnt = 0;
            while (queue.Count != 0)
            {
                KVP cur = queue.Dequeue();
                cnt += GetPassedPatternCount(cur.Key, cur.Value);
            }

            w.WriteLine(cnt);

            w.Flush();
            w.Close();
            r.Close();
        }

        private static int GetPassedPatternCount(int len, int ind)
        {
            string e = edges[ind];
            int cnt = 0;

            for (int i = 0; i < e.Length; i++)
            {
                while (len >= s.Length || (e[i] != s[len] && len != 0))
                    len = pfunc[len - 1];

                if (e[i] == s[len])
                    len++;
                else
                    len = 0;

                if (len >= s.Length)
                    cnt++;
                else if (pfunc.Count == len)
                    EvalStepPrefixFunctionForPattern();
            }

            for (int i = 0; i < children[ind].Count; i++)
            {
                if (s.Length <= (lengths[children[ind][i]] + edges[children[ind][i]].Length + len))
                    queue.Enqueue(new KVP(len, children[ind][i]));
            }

            return cnt;
        }

        private static void EvalStepPrefixFunctionForPattern()
        {
            int len = pfunc[pfunc.Count - 1];

            while (s[pfunc.Count] != s[len] && len != 0)
                len = pfunc[len - 1];

            if (s[pfunc.Count] == s[len])
                len++;
            else
                len = 0;

            pfunc.Add(len);
        }
    }
}


using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Threading;

namespace CodeForces
{
	class E
	{
		private const int HashCount = 4;
		private readonly long[] _hashBase = { 31, 3137, 93, 11 };
		private long[,] _hashPower;

		private long res;
		private List<int>[] mates;
		private long[,] hash;
		private int currentLength;
		private List<string> w;
		private string term;
		private long[] termHash;

		void Dfs( int u )
		{
			int tempLength = currentLength;

			foreach ( var v in mates[u] )
			{
				currentLength = tempLength;

				foreach ( var c in w[v - 1] )
				{
					for ( int i = 0; i < HashCount; ++i )
					{
						hash[i, currentLength] = ( currentLength == 0 ? c : c + hash[i, currentLength - 1] * _hashBase[i] );
					}
					++currentLength;
					if ( currentLength >= term.Length )
					{
						bool eq = true;

						for ( int i = 0; i < HashCount; ++i )
						{
							long cur = hash[i, currentLength - 1];
							if ( currentLength - 1 - term.Length >= 0 )
								cur -= hash[i, currentLength - 1 - term.Length] * _hashPower[i, term.Length];
							if ( cur != termHash[i] )
							{
								eq = false;
								break;
							}
						}

						if ( eq ) ++res;
					}
				}

				Dfs( v );
			}
		}

		long Solve( int n, List<int> p, List<string> words, string search )
		{
			term = search;
			w = words;
			int totalLength = 0;
			foreach ( var word in words )
			{
				totalLength += word.Length;
			}
			_hashPower = new long[HashCount, totalLength + 1];
			for ( int i = 0; i <= totalLength; ++i )
				for ( int j = 0; j < HashCount; ++j )
				{
					_hashPower[j, i] = ( i == 0 ? 1 : _hashPower[j, i - 1] * _hashBase[j] );
				}
			mates = new List<int>[n];
			for ( int i = 0; i < n; ++i ) mates[i] = new List<int>();
			for ( int i = 0; i < n - 1; ++i )
				mates[p[i] - 1].Add( i + 1 );
			res = 0;
			hash = new long[HashCount, totalLength + 1];
			currentLength = 0;
			termHash = new long[HashCount];
			for ( int i = 0; i < HashCount; ++i )
			{
				foreach ( var c in search )
				{
					termHash[i] = _hashBase[i] * termHash[i] + c;
				}
			}

			Dfs( 0 );
			return res;
		}

		private void Solve()
		{
			int n = NextInt();
			var p = new List<int>();
			var words = new List<string>();
			for ( int i = 0; i < n - 1; ++i )
			{
				p.Add( NextInt() );
				words.Add( NextLine().Trim() );
			}
			Out.WriteLine( Solve( n, p, words, NextLine().Trim() ) );
		}

		#region Local wireup

		public int NextInt()
		{
			return _in.NextInt();
		}

		public long NextLong()
		{
			return _in.NextLong();
		}

		public string NextLine()
		{
			return _in.NextLine();
		}

		public double NextDouble()
		{
			return _in.NextDouble();
		}

		readonly Scanner _in = new Scanner();
		static readonly TextWriter Out = Console.Out;

		void Start()
		{
#if !ONLINE_JUDGE
			var timer = new Stopwatch();
			timer.Start();
#endif
			var t = new Thread( Solve, 128 * 1024 * 1024 );
			t.Start();
			t.Join();
#if !ONLINE_JUDGE
			timer.Stop();
			Console.WriteLine( string.Format( CultureInfo.InvariantCulture, "Done in {0} seconds.\nPress <Enter> to exit.", timer.ElapsedMilliseconds / 1000.0 ) );
			Console.ReadLine();
#endif
		}

		static void Main()
		{
			new E().Start();
		}

		class Scanner : IDisposable
		{
			#region Fields

			readonly TextReader _reader;
			readonly int _bufferSize;
			readonly bool _closeReader;
			readonly char[] _buffer;
			int _length, _pos;

			#endregion

			#region .ctors

			public Scanner( TextReader reader, int bufferSize, bool closeReader )
			{
				_reader = reader;
				_bufferSize = bufferSize;
				_closeReader = closeReader;
				_buffer = new char[_bufferSize];
				FillBuffer( false );
			}

			public Scanner( TextReader reader, bool closeReader ) : this( reader, 1 << 16, closeReader ) { }

			public Scanner( string fileName ) : this( new StreamReader( fileName, Encoding.Default ), true ) { }

#if ONLINE_JUDGE
			public Scanner() : this( Console.In, false ) { }
#else
			public Scanner() : this( "input.txt" ) { }
#endif

			#endregion

			#region IDisposable Members

			public void Dispose()
			{
				if ( _closeReader )
				{
					_reader.Close();
				}
			}

			#endregion

			#region Properties

			public bool Eof
			{
				get
				{
					if ( _pos < _length ) return false;
					FillBuffer( false );
					return _pos >= _length;
				}
			}

			#endregion

			#region Methods

			private char NextChar()
			{
				if ( _pos < _length ) return _buffer[_pos++];
				FillBuffer( true );
				return _buffer[_pos++];
			}

			private char PeekNextChar()
			{
				if ( _pos < _length ) return _buffer[_pos];
				FillBuffer( true );
				return _buffer[_pos];
			}

			private void FillBuffer( bool throwOnEof )
			{
				_length = _reader.Read( _buffer, 0, _bufferSize );
				if ( throwOnEof && Eof )
				{
					throw new IOException( "Can't read beyond the end of file" );
				}
				_pos = 0;
			}

			public int NextInt()
			{
				var neg = false;
				int res = 0;
				SkipWhitespaces();
				if ( !Eof && PeekNextChar() == '-' )
				{
					neg = true;
					_pos++;
				}
				while ( !Eof && !IsWhitespace( PeekNextChar() ) )
				{
					var c = NextChar();
					if ( c < '0' || c > '9' ) throw new ArgumentException( "Illegal character" );
					res = 10 * res + c - '0';
				}
				return neg ? -res : res;
			}

			public long NextLong()
			{
				var neg = false;
				long res = 0;
				SkipWhitespaces();
				if ( !Eof && PeekNextChar() == '-' )
				{
					neg = true;
					_pos++;
				}
				while ( !Eof && !IsWhitespace( PeekNextChar() ) )
				{
					var c = NextChar();
					if ( c < '0' || c > '9' ) throw new ArgumentException( "Illegal character" );
					res = 10 * res + c - '0';
				}
				return neg ? -res : res;
			}

			public string NextLine()
			{
				SkipUntilNextLine();
				if ( Eof ) return "";
				var builder = new StringBuilder();
				while ( !Eof && !IsEndOfLine( PeekNextChar() ) )
				{
					builder.Append( NextChar() );
				}
				return builder.ToString();
			}

			public double NextDouble()
			{
				SkipWhitespaces();
				var builder = new StringBuilder();
				while ( !Eof && !IsWhitespace( PeekNextChar() ) )
				{
					builder.Append( NextChar() );
				}
				return double.Parse( builder.ToString(), CultureInfo.InvariantCulture );
			}

			private void SkipWhitespaces()
			{
				while ( !Eof && IsWhitespace( PeekNextChar() ) )
				{
					++_pos;
				}
			}

			private void SkipUntilNextLine()
			{
				while ( !Eof && IsEndOfLine( PeekNextChar() ) )
				{
					++_pos;
				}
			}

			private static bool IsWhitespace( char c )
			{
				return c == ' ' || c == '\t' || c == '\n' || c == '\r';
			}

			private static bool IsEndOfLine( char c )
			{
				return c == '\n' || c == '\r';
			}

			#endregion
		}

		#endregion
	}
}


using System;
using System.Collections;
using System.Threading;

class e
{
  ArrayList[] c, s;
  string text;
  int len, cnt = 0;
  int[] p;
  int[,] next;

  void go( int v, int x )
  {
    for (int i = 0; i < c[v].Count; i++)
    {
      int y = x;
      string t = (string)s[v][i];
      for (int j = 0; j < t.Length; j++)
      {
        y = next[y, t[j] - 'a'];
        if (y == len)
          cnt++;
      }
      go((int)c[v][i], y);
    }
  }

  static void Main(string[] args)
  {
    e o = new e();
    Thread oThread = new Thread(new ThreadStart(o.run), 1 << 25);
    oThread.Start();
  }

  void run()
  {
    int n = int.Parse(Console.ReadLine());
    c = new ArrayList[n];
    s = new ArrayList[n];
    for (int i = 0; i < n; i++)
    {
      c[i] = new ArrayList();
      s[i] = new ArrayList();
    }
    for (int i = 1; i < n; i++)
    {
      string[] tmp = Console.ReadLine().Split(' ');
      int pa = int.Parse(tmp[0]) - 1;
      c[pa].Add(i);
      s[pa].Add(tmp[1]);
    }

    text = Console.ReadLine();
    len = text.Length;
    p = new int[len];
    next = new int[len + 1, 26];
    p[0] = 0;
    for (int i = 0; i < 26; i++)
      next[0, i] = (text[0] == i + 'a' ? 1 : 0);
    for (int x = 1; x < len; x++)
    {
      p[x] = next[p[x - 1], text[x] - 'a'];
      for (int i = 0; i < 26; i++)
        next[x, i] = (text[x] == i + 'a' ? x + 1 : next[p[x - 1], i]);
    }
    for (int i = 0; i < 26; i++)
      next[len, i] = next[p[len - 1], i];
    go(0, 0);
    Console.WriteLine(cnt);
  }
}


using System;
using System.Collections;

class e
{
  ArrayList[] c, s;
  string text;
  int len, cnt = 0;
  int[] p;
  int[,] next;

  void go( int v, int x )
  {
    for (int i = 0; i < c[v].Count; i++)
    {
      int y = x;
      string t = (string)s[v][i];
      for (int j = 0; j < t.Length; j++)
      {
        y = next[y, t[j] - 'a'];
        if (y == len)
          cnt++;
      }
      go((int)c[v][i], y);
    }
  }

  static void Main(string[] args)
  {
    new e().run();
  }

  void run()
  {
    int n = int.Parse(Console.ReadLine());
    c = new ArrayList[n];
    s = new ArrayList[n];
    for (int i = 0; i < n; i++)
    {
      c[i] = new ArrayList();
      s[i] = new ArrayList();
    }
    for (int i = 1; i < n; i++)
    {
      string[] tmp = Console.ReadLine().Split(' ');
      int pa = int.Parse(tmp[0]) - 1;
      c[pa].Add(i);
      s[pa].Add(tmp[1]);
    }

    text = Console.ReadLine();
    len = text.Length;
    p = new int[len];
    next = new int[len + 1, 26];
    p[0] = 0;
    for (int i = 0; i < 26; i++)
      next[0, i] = (text[0] == i + 'a' ? 1 : 0);
    for (int x = 1; x < len; x++)
    {
      p[x] = next[p[x - 1], text[x] - 'a'];
      for (int i = 0; i < 26; i++)
        next[x, i] = (text[x] == i + 'a' ? x + 1 : next[p[x - 1], i]);
    }
    for (int i = 0; i < 26; i++)
      next[len, i] = next[p[len - 1], i];
    go(0, 0);
    Console.WriteLine(cnt);
  }
}


//

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;

namespace sar_cs
{
    static class Helpers
    {
        public static string Join<T>(this IEnumerable<T> objs, string sep)
        {
            var sb = new StringBuilder();
            foreach (var s in objs) { if (sb.Length != 0) sb.Append(sep); sb.Append(s); }
            return sb.ToString();
        }

        public static string AsString(this double a, int digits)
        {
            return a.ToString("F" + digits, CultureInfo.InvariantCulture);
        }

        public static T[] Copy<T>(this T[] a)
        {
            var b = new T[a.Length]; a.CopyTo(b, 0); return b;
        }

        public static T[][] Copy<T>(this T[][] a)
        {
            return a.Select(z => z.Copy()).ToArray();
        }

        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> collection)
        {
            if (collection.Take(11).Count() <= 10) return collection;
            var a = collection is T[] ? (T[])collection : collection.ToArray();
            int N = a.Length;
            for (int i = 0, p1 = 0, p2 = 1; i < N / 2; i++)
            {
                p1 += 3; if (p1 >= N) p1 -= N;
                p2 -= 7; if (p2 < 0) p2 += N;
                T t = a[p1]; a[p1] = a[p2]; a[p2] = t;
            }
            return a;
        }

        public static string AsString<T>(this T[,] a, string separator)
        {
            var sb = new StringBuilder();
            int n0 = a.GetLength(0);
            int n1 = a.GetLength(1);
            for (int r = 0; r < n0; r++)
            {
                for (int c = 0; c < n1; c++)
                {
                    if (c != 0) sb.Append(separator); sb.Append(a[r, c]);
                }
                sb.AppendLine();
            }
            //sb.AppendLine();
            return sb.ToString();
        }

        public static Stopwatch SW = Stopwatch.StartNew();
    }

    public class Program
    {
        #region Helpers

        public class Parser
        {
            const int BufSize = 8000;
            TextReader s;
            char[] buf = new char[BufSize];
            int bufPos;
            int bufDataSize;
            bool eos;   // eos read to buffer
            int lastChar = -2;
            int nextChar = -2;
            StringBuilder sb = new StringBuilder();

            void FillBuf()
            {
                if (eos) return;
                bufDataSize = s.Read(buf, 0, BufSize);
                if (bufDataSize == 0) eos = true;
                bufPos = 0;
            }

            int Read()
            {
                if (nextChar != -2)
                {
                    lastChar = nextChar;
                    nextChar = -2;
                }
                else
                {
                    if (bufPos == bufDataSize) FillBuf();
                    if (eos) lastChar = -1; else lastChar = buf[bufPos++];
                }
                return lastChar;
            }

            void Back()
            {
                if (lastChar == -2) throw new Exception();  // no chars were ever read
                nextChar = lastChar;
            }

            public Parser()
            {
                s = Console.In;
            }

            public int ReadInt()
            {
                int res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public long ReadLong()
            {
                long res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public int ReadLnInt()
            {
                int res = ReadInt(); ReadLine(); return res;
            }

            public long ReadLnLong()
            {
                long res = ReadLong(); ReadLine(); return res;
            }

            public double ReadDouble()
            {
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));

                int sign = 1;
                if (c == '-') { sign = -1; c = Read(); }

                sb.Length = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    sb.Append((char)c); c = Read();
                }

                Back();
                var res = double.Parse(sb.ToString(), CultureInfo.InvariantCulture);
                return res * sign;
            }

            public string ReadLine()
            {
                int c = Read();
                sb.Length = 0;
                while (c != -1 && c != 13 && c != 10)
                {
                    sb.Append((char)c); c = Read();
                }
                if (c == 13) { c = Read(); if (c != 10) Back(); }
                return sb.ToString();
            }

            public bool SeekEOF
            {
                get
                {
                L0:
                    int c = Read();
                    if (c == -1) return true;
                    if (char.IsWhiteSpace((char)c)) goto L0;
                    Back();
                    return false;
                }
            }

            public int[] ReadIntArr(int n)
            {
                var a = new int[n]; for (int i = 0; i < n; i++) a[i] = ReadInt(); return a;
            }

            public long[] ReadLongArr(int n)
            {
                var a = new long[n]; for (int i = 0; i < n; i++) a[i] = ReadLong(); return a;
            }
        }

        class Writer
        {
            const int BufSize = 32000;
            char[] buf = new char[BufSize];
            int bufPos = 0;
            TextWriter output = Console.Out;
            Stack<char> st = new Stack<char>();

            public void Flush()
            {
                output.Write(buf, 0, bufPos); bufPos = 0;
                output.Flush();
            }

            void BufSpaceNeeded(int n)
            {
                if (n > BufSize) throw new ArgumentOutOfRangeException("n");
                if (bufPos + n > BufSize) { output.Write(buf, 0, bufPos); bufPos = 0; }
            }

            public void Write(char a)
            {
                BufSpaceNeeded(1);
                buf[bufPos++] = a;
            }

            public void Write(string s)
            {
                int l = s.Length;
                for (int x = 0; x < l; )
                {
                    int t = Math.Min(l - x, BufSize - bufPos);
                    s.CopyTo(x, buf, bufPos, t); bufPos += t; x += t;
                    if (x < l) BufSpaceNeeded(1);   // force flush
                }
            }

            public void WriteLine()
            {
                BufSpaceNeeded(2);
                buf[bufPos++] = '\r'; buf[bufPos++] = '\n';
            }

            public void Write(object o) { Write(o.ToString()); }

            public void Write(uint a)
            {
                BufSpaceNeeded(10);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(ulong a)
            {
                BufSpaceNeeded(20);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(int a)
            {
                BufSpaceNeeded(11);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((uint)a);
            }

            public void Write(long a)
            {
                BufSpaceNeeded(21);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((ulong)a);
            }

            public void Write(double a, int digits)
            {
                Write(a.AsString(digits));
            }

            public void WriteLine(int a) { Write(a); WriteLine(); }
            public void WriteLine(long a) { Write(a); WriteLine(); }
            public void WriteLine(uint a) { Write(a); WriteLine(); }
            public void WriteLine(ulong a) { Write(a); WriteLine(); }
            public void WriteLine(string s) { Write(s); WriteLine(); }
            public void WriteLine(object o) { Write(o); WriteLine(); }
        }

        static void pe() { Console.WriteLine("???"); Environment.Exit(0); }
        static void re() { throw new Exception(); }
        static void Swap<T>(ref T a, ref T b) { T t = a; a = b; b = t; }
        static int Sqr(int a) { return a * a; }
        static long SqrL(int a) { return (long)a * a; }
        static long Sqr(long a) { return a * a; }
        static double Sqr(double a) { return a * a; }
        static double Sqrt(double a) { return Math.Sqrt(a); }

        static StringBuilder sb = new StringBuilder();
        static Random rnd = new Random(5);

        #endregion Helpers

        struct IncrementalHash64
        {
            const uint Key = 0x080883dd;

            public ulong Value;

            ulong c1;
            int windowSize;

            public IncrementalHash64(int windowSize)
            {
                if (windowSize <= 0) throw new ArgumentException();
                this.windowSize = windowSize;

                c1 = Pow(Key, (uint)windowSize);

                Value = 0;
            }

            public IncrementalHash64(int[] initialInput, int initialInputOffset, int initialInputCount)
                : this(initialInputCount)
            {
                Init_Next(initialInput, initialInputOffset, initialInputCount);
            }

            public int WindowSize { get { return windowSize; } }

            public void Init(int[] input, int offset)
            {
                Value = 0;
                Init_Next(input, offset, windowSize);
            }

            public void Init_Next(int[] input, int offset, int count)
            {
                for (int i = 0; i < count; i++)
                    Value = (Value + (uint)input[i + offset]) * Key;
            }

            public void Init_Next(int nextchar)
            {
                Value = (Value + (uint)nextchar) * Key;
            }

            public void Next(int nextChar, int releaseChar)
            {
                Value = (Value - c1 * (uint)releaseChar + (uint)nextChar) * Key;
            }

            private static ulong Pow(uint a, uint pow)
            {
                ulong res = 1;
                for (uint bit = 0x80000000; bit != 0; bit >>= 1)
                {
                    res *= res;
                    if ((pow & bit) != 0) res *= a;
                }
                return res;
            }

            public static ulong CalcHash(int[] input, int offset, int count)
            {
                ulong res = 0;
                for (int i = 0; i < count; i++)
                    res = (res + (uint)input[i + offset]) * Key;
                return res;
            }

        }

        class Edge
        {
            public string s;
            public int w;
        }

        static List<Edge>[] nodes;
        static int[] path;
        static int[] t;
        static ulong t_hash;

        static long f(int depth, Edge e, IncrementalHash64 ih)
        {
            long res = 0;
            foreach (var c in e.s)
            {
                if (depth < t.Length) ih.Init_Next(c);
                else ih.Next(c, path[depth - t.Length]);
                path[depth++] = c;

                if (ih.Value==t_hash)
                    res++;
            }

            foreach (var child in nodes[e.w])
                res += f(depth, child, ih);

            return res;
        }

        static void Main(string[] args)
        {
            checked
            {
#if !ONLINE_JUDGE
                Console.SetIn(File.OpenText("_input"));
#endif
                //Console.SetOut(File.CreateText("_output"));
                var parser = new Parser();
                var o = new Writer();
                try
                {
                    while (!parser.SeekEOF)
                    {
                        int n = parser.ReadInt();
                        nodes = new List<Edge>[n + 1];
                        for (int i = 1; i <= n; i++) nodes[i] = new List<Edge>(0);
                        for (int i = 2; i <= n; i++)
                        {
                            int p = parser.ReadInt();
                            var s = parser.ReadLine().Trim();
                            nodes[p].Add(new Edge { w = i, s = s });
                        }
                        t = parser.ReadLine().Select(c => (int)c).ToArray();

                        t_hash = IncrementalHash64.CalcHash(t, 0, t.Length);

                        path = new int[300 * 1000 + 5];

                        var ih = new IncrementalHash64(t, 0, t.Length);
                        var res = f(0, new Edge { w = 1, s = "" }, new IncrementalHash64(t.Length));

                        o.WriteLine(res);
                    }
                }
                finally
                {
                    o.Flush();
                }
            }
        }

        //static void Main(string[] args)
        //{
        //    new Thread(Main2, 50 << 20).Start();
        //}
    }
}


//

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;

namespace sar_cs
{
    static class Helpers
    {
        public static string Join<T>(this IEnumerable<T> objs, string sep)
        {
            var sb = new StringBuilder();
            foreach (var s in objs) { if (sb.Length != 0) sb.Append(sep); sb.Append(s); }
            return sb.ToString();
        }

        public static string AsString(this double a, int digits)
        {
            return a.ToString("F" + digits, CultureInfo.InvariantCulture);
        }

        public static T[] Copy<T>(this T[] a)
        {
            var b = new T[a.Length]; a.CopyTo(b, 0); return b;
        }

        public static T[][] Copy<T>(this T[][] a)
        {
            return a.Select(z => z.Copy()).ToArray();
        }

        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> collection)
        {
            if (collection.Take(11).Count() <= 10) return collection;
            var a = collection is T[] ? (T[])collection : collection.ToArray();
            int N = a.Length;
            for (int i = 0, p1 = 0, p2 = 1; i < N / 2; i++)
            {
                p1 += 3; if (p1 >= N) p1 -= N;
                p2 -= 7; if (p2 < 0) p2 += N;
                T t = a[p1]; a[p1] = a[p2]; a[p2] = t;
            }
            return a;
        }

        public static string AsString<T>(this T[,] a, string separator)
        {
            var sb = new StringBuilder();
            int n0 = a.GetLength(0);
            int n1 = a.GetLength(1);
            for (int r = 0; r < n0; r++)
            {
                for (int c = 0; c < n1; c++)
                {
                    if (c != 0) sb.Append(separator); sb.Append(a[r, c]);
                }
                sb.AppendLine();
            }
            //sb.AppendLine();
            return sb.ToString();
        }

        public static Stopwatch SW = Stopwatch.StartNew();
    }

    public class Program
    {
        #region Helpers

        public class Parser
        {
            const int BufSize = 8000;
            TextReader s;
            char[] buf = new char[BufSize];
            int bufPos;
            int bufDataSize;
            bool eos;   // eos read to buffer
            int lastChar = -2;
            int nextChar = -2;
            StringBuilder sb = new StringBuilder();

            void FillBuf()
            {
                if (eos) return;
                bufDataSize = s.Read(buf, 0, BufSize);
                if (bufDataSize == 0) eos = true;
                bufPos = 0;
            }

            int Read()
            {
                if (nextChar != -2)
                {
                    lastChar = nextChar;
                    nextChar = -2;
                }
                else
                {
                    if (bufPos == bufDataSize) FillBuf();
                    if (eos) lastChar = -1; else lastChar = buf[bufPos++];
                }
                return lastChar;
            }

            void Back()
            {
                if (lastChar == -2) throw new Exception();  // no chars were ever read
                nextChar = lastChar;
            }

            public Parser()
            {
                s = Console.In;
            }

            public int ReadInt()
            {
                int res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public long ReadLong()
            {
                long res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public int ReadLnInt()
            {
                int res = ReadInt(); ReadLine(); return res;
            }

            public long ReadLnLong()
            {
                long res = ReadLong(); ReadLine(); return res;
            }

            public double ReadDouble()
            {
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));

                int sign = 1;
                if (c == '-') { sign = -1; c = Read(); }

                sb.Length = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    sb.Append((char)c); c = Read();
                }

                Back();
                var res = double.Parse(sb.ToString(), CultureInfo.InvariantCulture);
                return res * sign;
            }

            public string ReadLine()
            {
                int c = Read();
                sb.Length = 0;
                while (c != -1 && c != 13 && c != 10)
                {
                    sb.Append((char)c); c = Read();
                }
                if (c == 13) { c = Read(); if (c != 10) Back(); }
                return sb.ToString();
            }

            public bool SeekEOF
            {
                get
                {
                L0:
                    int c = Read();
                    if (c == -1) return true;
                    if (char.IsWhiteSpace((char)c)) goto L0;
                    Back();
                    return false;
                }
            }

            public int[] ReadIntArr(int n)
            {
                var a = new int[n]; for (int i = 0; i < n; i++) a[i] = ReadInt(); return a;
            }

            public long[] ReadLongArr(int n)
            {
                var a = new long[n]; for (int i = 0; i < n; i++) a[i] = ReadLong(); return a;
            }
        }

        class Writer
        {
            const int BufSize = 32000;
            char[] buf = new char[BufSize];
            int bufPos = 0;
            TextWriter output = Console.Out;
            Stack<char> st = new Stack<char>();

            public void Flush()
            {
                output.Write(buf, 0, bufPos); bufPos = 0;
                output.Flush();
            }

            void BufSpaceNeeded(int n)
            {
                if (n > BufSize) throw new ArgumentOutOfRangeException("n");
                if (bufPos + n > BufSize) { output.Write(buf, 0, bufPos); bufPos = 0; }
            }

            public void Write(char a)
            {
                BufSpaceNeeded(1);
                buf[bufPos++] = a;
            }

            public void Write(string s)
            {
                int l = s.Length;
                for (int x = 0; x < l; )
                {
                    int t = Math.Min(l - x, BufSize - bufPos);
                    s.CopyTo(x, buf, bufPos, t); bufPos += t; x += t;
                    if (x < l) BufSpaceNeeded(1);   // force flush
                }
            }

            public void WriteLine()
            {
                BufSpaceNeeded(2);
                buf[bufPos++] = '\r'; buf[bufPos++] = '\n';
            }

            public void Write(object o) { Write(o.ToString()); }

            public void Write(uint a)
            {
                BufSpaceNeeded(10);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(ulong a)
            {
                BufSpaceNeeded(20);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(int a)
            {
                BufSpaceNeeded(11);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((uint)a);
            }

            public void Write(long a)
            {
                BufSpaceNeeded(21);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((ulong)a);
            }

            public void Write(double a, int digits)
            {
                Write(a.AsString(digits));
            }

            public void WriteLine(int a) { Write(a); WriteLine(); }
            public void WriteLine(long a) { Write(a); WriteLine(); }
            public void WriteLine(uint a) { Write(a); WriteLine(); }
            public void WriteLine(ulong a) { Write(a); WriteLine(); }
            public void WriteLine(string s) { Write(s); WriteLine(); }
            public void WriteLine(object o) { Write(o); WriteLine(); }
        }

        static void pe() { Console.WriteLine("???"); Environment.Exit(0); }
        static void re() { throw new Exception(); }
        static void Swap<T>(ref T a, ref T b) { T t = a; a = b; b = t; }
        static int Sqr(int a) { return a * a; }
        static long SqrL(int a) { return (long)a * a; }
        static long Sqr(long a) { return a * a; }
        static double Sqr(double a) { return a * a; }
        static double Sqrt(double a) { return Math.Sqrt(a); }

        static StringBuilder sb = new StringBuilder();
        static Random rnd = new Random(5);

        #endregion Helpers

        struct IncrementalHash64
        {
            const uint Key = 0x080883dd;

            public ulong Value;

            ulong c1;
            int windowSize;

            public IncrementalHash64(int windowSize)
            {
                if (windowSize <= 0) throw new ArgumentException();
                this.windowSize = windowSize;

                c1 = Pow(Key, (uint)windowSize);

                Value = 0;
            }

            public IncrementalHash64(int[] initialInput, int initialInputOffset, int initialInputCount)
                : this(initialInputCount)
            {
                Init_Next(initialInput, initialInputOffset, initialInputCount);
            }

            public int WindowSize { get { return windowSize; } }

            public void Init(int[] input, int offset)
            {
                Value = 0;
                Init_Next(input, offset, windowSize);
            }

            public void Init_Next(int[] input, int offset, int count)
            {
                for (int i = 0; i < count; i++)
                    Value = (Value + (uint)input[i + offset]) * Key;
            }

            public void Init_Next(int nextchar)
            {
                Value = (Value + (uint)nextchar) * Key;
            }

            public void Next(int nextChar, int releaseChar)
            {
                Value = (Value - c1 * (uint)releaseChar + (uint)nextChar) * Key;
            }

            private static ulong Pow(uint a, uint pow)
            {
                ulong res = 1;
                for (uint bit = 0x80000000; bit != 0; bit >>= 1)
                {
                    res *= res;
                    if ((pow & bit) != 0) res *= a;
                }
                return res;
            }

            public static ulong CalcHash(int[] input, int offset, int count)
            {
                ulong res = 0;
                for (int i = 0; i < count; i++)
                    res = (res + (uint)input[i + offset]) * Key;
                return res;
            }

        }

        class Edge
        {
            public string s;
            public int w;
        }

        static List<Edge>[] nodes;
        static int[] path;
        static int[] t;
        static ulong t_hash;

        static long f(int depth, Edge e, IncrementalHash64 ih)
        {
            long res = 0;
            foreach (var c in e.s)
            {
                if (depth < t.Length) ih.Init_Next(c);
                else ih.Next(c, path[depth - t.Length]);
                path[depth++] = c;

                if (ih.Value == t_hash)
                    res++;
            }

            foreach (var child in nodes[e.w])
                res += f(depth, child, ih);

            return res;
        }

        static void Main2()
        {
            checked
            {
#if !ONLINE_JUDGE
                Console.SetIn(File.OpenText("_input"));
#endif
                //Console.SetOut(File.CreateText("_output"));
                var parser = new Parser();
                var o = new Writer();
                try
                {
                    while (!parser.SeekEOF)
                    {
                        int n = parser.ReadInt();
                        //n = 100000;
                        nodes = new List<Edge>[n + 1];
                        for (int i = 1; i <= n; i++) nodes[i] = new List<Edge>(0);
                        for (int i = 2; i <= n; i++)
                        {
                            int p = parser.ReadInt();
                            var s = parser.ReadLine().Trim();
                            //int p = i - 1;
                            //var s = "zz";
                            nodes[p].Add(new Edge { w = i, s = s });
                        }
                        t = parser.ReadLine().Select(c => (int)c).ToArray();
                        //t = "aaa".Select(c => (int)c).ToArray();

                        t_hash = IncrementalHash64.CalcHash(t, 0, t.Length);

                        path = new int[300 * 1000 + 5];

                        var ih = new IncrementalHash64(t, 0, t.Length);
                        var res = f(0, new Edge { w = 1, s = "" }, new IncrementalHash64(t.Length));

                        o.WriteLine(res);
                    }
                }
                finally
                {
                    o.Flush();
                }
            }
        }

        static void Main()
        {
            new Thread(Main2, 50 << 20).Start();
        }
    }
}


//

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;

namespace sar_cs
{
    static class Helpers
    {
        public static string Join<T>(this IEnumerable<T> objs, string sep)
        {
            var sb = new StringBuilder();
            foreach (var s in objs) { if (sb.Length != 0) sb.Append(sep); sb.Append(s); }
            return sb.ToString();
        }

        public static string AsString(this double a, int digits)
        {
            return a.ToString("F" + digits, CultureInfo.InvariantCulture);
        }

        public static T[] Copy<T>(this T[] a)
        {
            var b = new T[a.Length]; a.CopyTo(b, 0); return b;
        }

        public static T[][] Copy<T>(this T[][] a)
        {
            return a.Select(z => z.Copy()).ToArray();
        }

        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> collection)
        {
            if (collection.Take(11).Count() <= 10) return collection;
            var a = collection is T[] ? (T[])collection : collection.ToArray();
            int N = a.Length;
            for (int i = 0, p1 = 0, p2 = 1; i < N / 2; i++)
            {
                p1 += 3; if (p1 >= N) p1 -= N;
                p2 -= 7; if (p2 < 0) p2 += N;
                T t = a[p1]; a[p1] = a[p2]; a[p2] = t;
            }
            return a;
        }

        public static string AsString<T>(this T[,] a, string separator)
        {
            var sb = new StringBuilder();
            int n0 = a.GetLength(0);
            int n1 = a.GetLength(1);
            for (int r = 0; r < n0; r++)
            {
                for (int c = 0; c < n1; c++)
                {
                    if (c != 0) sb.Append(separator); sb.Append(a[r, c]);
                }
                sb.AppendLine();
            }
            //sb.AppendLine();
            return sb.ToString();
        }

        public static Stopwatch SW = Stopwatch.StartNew();
    }

    public class Program
    {
        #region Helpers

        public class Parser
        {
            const int BufSize = 8000;
            TextReader s;
            char[] buf = new char[BufSize];
            int bufPos;
            int bufDataSize;
            bool eos;   // eos read to buffer
            int lastChar = -2;
            int nextChar = -2;
            StringBuilder sb = new StringBuilder();

            void FillBuf()
            {
                if (eos) return;
                bufDataSize = s.Read(buf, 0, BufSize);
                if (bufDataSize == 0) eos = true;
                bufPos = 0;
            }

            int Read()
            {
                if (nextChar != -2)
                {
                    lastChar = nextChar;
                    nextChar = -2;
                }
                else
                {
                    if (bufPos == bufDataSize) FillBuf();
                    if (eos) lastChar = -1; else lastChar = buf[bufPos++];
                }
                return lastChar;
            }

            void Back()
            {
                if (lastChar == -2) throw new Exception();  // no chars were ever read
                nextChar = lastChar;
            }

            public Parser()
            {
                s = Console.In;
            }

            public int ReadInt()
            {
                int res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public long ReadLong()
            {
                long res = 0;
                int sign = -1;
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));
                if (c == '-') { sign = 1; c = Read(); }
                int len = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    if ((uint)(c -= 48) >= 10) throw new FormatException();
                    len++;
                    res = checked(res * 10 - c);
                    c = Read();
                }
                if (len == 0) throw new FormatException();
                Back();
                return checked(res * sign);
            }

            public int ReadLnInt()
            {
                int res = ReadInt(); ReadLine(); return res;
            }

            public long ReadLnLong()
            {
                long res = ReadLong(); ReadLine(); return res;
            }

            public double ReadDouble()
            {
                int c;
                do { c = Read(); } while (c >= 0 && char.IsWhiteSpace((char)c));

                int sign = 1;
                if (c == '-') { sign = -1; c = Read(); }

                sb.Length = 0;
                while (c >= 0 && !char.IsWhiteSpace((char)c))
                {
                    sb.Append((char)c); c = Read();
                }

                Back();
                var res = double.Parse(sb.ToString(), CultureInfo.InvariantCulture);
                return res * sign;
            }

            public string ReadLine()
            {
                int c = Read();
                sb.Length = 0;
                while (c != -1 && c != 13 && c != 10)
                {
                    sb.Append((char)c); c = Read();
                }
                if (c == 13) { c = Read(); if (c != 10) Back(); }
                return sb.ToString();
            }

            public bool SeekEOF
            {
                get
                {
                L0:
                    int c = Read();
                    if (c == -1) return true;
                    if (char.IsWhiteSpace((char)c)) goto L0;
                    Back();
                    return false;
                }
            }

            public int[] ReadIntArr(int n)
            {
                var a = new int[n]; for (int i = 0; i < n; i++) a[i] = ReadInt(); return a;
            }

            public long[] ReadLongArr(int n)
            {
                var a = new long[n]; for (int i = 0; i < n; i++) a[i] = ReadLong(); return a;
            }
        }

        class Writer
        {
            const int BufSize = 32000;
            char[] buf = new char[BufSize];
            int bufPos = 0;
            TextWriter output = Console.Out;
            Stack<char> st = new Stack<char>();

            public void Flush()
            {
                output.Write(buf, 0, bufPos); bufPos = 0;
                output.Flush();
            }

            void BufSpaceNeeded(int n)
            {
                if (n > BufSize) throw new ArgumentOutOfRangeException("n");
                if (bufPos + n > BufSize) { output.Write(buf, 0, bufPos); bufPos = 0; }
            }

            public void Write(char a)
            {
                BufSpaceNeeded(1);
                buf[bufPos++] = a;
            }

            public void Write(string s)
            {
                int l = s.Length;
                for (int x = 0; x < l; )
                {
                    int t = Math.Min(l - x, BufSize - bufPos);
                    s.CopyTo(x, buf, bufPos, t); bufPos += t; x += t;
                    if (x < l) BufSpaceNeeded(1);   // force flush
                }
            }

            public void WriteLine()
            {
                BufSpaceNeeded(2);
                buf[bufPos++] = '\r'; buf[bufPos++] = '\n';
            }

            public void Write(object o) { Write(o.ToString()); }

            public void Write(uint a)
            {
                BufSpaceNeeded(10);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(ulong a)
            {
                BufSpaceNeeded(20);
                do { st.Push((char)(a % 10 + 48)); a /= 10; } while (a != 0);
                while (st.Count != 0) buf[bufPos++] = st.Pop();
            }

            public void Write(int a)
            {
                BufSpaceNeeded(11);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((uint)a);
            }

            public void Write(long a)
            {
                BufSpaceNeeded(21);
                if (a < 0) { buf[bufPos++] = '-'; a = -a; }
                Write((ulong)a);
            }

            public void Write(double a, int digits)
            {
                Write(a.AsString(digits));
            }

            public void WriteLine(int a) { Write(a); WriteLine(); }
            public void WriteLine(long a) { Write(a); WriteLine(); }
            public void WriteLine(uint a) { Write(a); WriteLine(); }
            public void WriteLine(ulong a) { Write(a); WriteLine(); }
            public void WriteLine(string s) { Write(s); WriteLine(); }
            public void WriteLine(object o) { Write(o); WriteLine(); }
        }

        static void pe() { Console.WriteLine("???"); Environment.Exit(0); }
        static void re() { throw new Exception(); }
        static void Swap<T>(ref T a, ref T b) { T t = a; a = b; b = t; }
        static int Sqr(int a) { return a * a; }
        static long SqrL(int a) { return (long)a * a; }
        static long Sqr(long a) { return a * a; }
        static double Sqr(double a) { return a * a; }
        static double Sqrt(double a) { return Math.Sqrt(a); }

        static StringBuilder sb = new StringBuilder();
        static Random rnd = new Random(5);

        #endregion Helpers

        struct IncrementalHash64
        {
            const uint Key = 0x080883dd;

            public ulong Value;

            ulong c1;
            int windowSize;

            public IncrementalHash64(int windowSize)
            {
                if (windowSize <= 0) throw new ArgumentException();
                this.windowSize = windowSize;

                c1 = Pow(Key, (uint)windowSize);

                Value = 0;
            }

            public IncrementalHash64(int[] initialInput, int initialInputOffset, int initialInputCount)
                : this(initialInputCount)
            {
                Init_Next(initialInput, initialInputOffset, initialInputCount);
            }

            public int WindowSize { get { return windowSize; } }

            public void Init(int[] input, int offset)
            {
                Value = 0;
                Init_Next(input, offset, windowSize);
            }

            public void Init_Next(int[] input, int offset, int count)
            {
                for (int i = 0; i < count; i++)
                    Value = (Value + (uint)input[i + offset]) * Key;
            }

            public void Init_Next(int nextchar)
            {
                Value = (Value + (uint)nextchar) * Key;
            }

            public void Next(int nextChar, int releaseChar)
            {
                Value = (Value - c1 * (uint)releaseChar + (uint)nextChar) * Key;
            }

            private static ulong Pow(uint a, uint pow)
            {
                ulong res = 1;
                for (uint bit = 0x80000000; bit != 0; bit >>= 1)
                {
                    res *= res;
                    if ((pow & bit) != 0) res *= a;
                }
                return res;
            }

            public static ulong CalcHash(int[] input, int offset, int count)
            {
                ulong res = 0;
                for (int i = 0; i < count; i++)
                    res = (res + (uint)input[i + offset]) * Key;
                return res;
            }

        }

        class Edge
        {
            public string s;
            public int w;
        }

        static List<Edge>[] nodes;
        static int[] path;
        static int[] t;
        static ulong t_hash;

        static long f(int depth, Edge e, IncrementalHash64 ih)
        {
            long res = 0;
            foreach (var c in e.s)
            {
                if (depth < t.Length) ih.Init_Next(c);
                else ih.Next(c, path[depth - t.Length]);
                path[depth++] = c;

                if (ih.Value==t_hash)
                    res++;
            }

            foreach (var child in nodes[e.w])
                res += f(depth, child, ih);

            return res;
        }

        static void Main2()
        {
            checked
            {
#if !ONLINE_JUDGE
                Console.SetIn(File.OpenText("_input"));
#endif
                //Console.SetOut(File.CreateText("_output"));
                var parser = new Parser();
                var o = new Writer();
                try
                {
                    while (!parser.SeekEOF)
                    {
                        int n = parser.ReadInt();
                        nodes = new List<Edge>[n + 1];
                        for (int i = 1; i <= n; i++) nodes[i] = new List<Edge>(0);
                        for (int i = 2; i <= n; i++)
                        {
                            int p = parser.ReadInt();
                            var s = parser.ReadLine().Trim();
                            nodes[p].Add(new Edge { w = i, s = s });
                        }
                        t = parser.ReadLine().Select(c => (int)c).ToArray();

                        t_hash = IncrementalHash64.CalcHash(t, 0, t.Length);

                        path = new int[300 * 1000 + 5];

                        var ih = new IncrementalHash64(t, 0, t.Length);
                        var res = f(0, new Edge { w = 1, s = "" }, new IncrementalHash64(t.Length));

                        o.WriteLine(res);
                    }
                }
                finally
                {
                    o.Flush();
                }
            }
        }

        static void Main()
        {
            new Thread(Main2, 10 << 20).Start();
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 150;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                while (j >= 0 && (j == T.Length || S[v][i] != T[j])) j = LCP[j];
                if (j == T.Length - 1) ret++;
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = (i != T.Length && T[i] == T[j]) ? LCP[j] : j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 150;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                if (j == T.Length) j = LCP[j];
                while (j >= 0 && S[v][i] != T[j]) j = LCP[j];
                if (j == T.Length - 1) ret++;
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = (j == sz(T) || T[i] == T[j]) ? LCP[j] : j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 150;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                while (j >= 0 && (j == T.Length || S[v][i] != T[j])) j = LCP[j];
                if (j == T.Length - 1) ret++;
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 150;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                while (j >= 0 && (j == T.Length || S[v][i] != T[j])) j = LCP[j];
                if (j == T.Length - 1) ret++;
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = (i == T.Length || T[i] == T[j]) ? LCP[j] : j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 10;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                while (j >= 0 && S[v][i] != T[j]) j = LCP[j];
                if (j == T.Length - 1) {
                    ret++;
                    j = LCP[j];
                }
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using static System.Diagnostics.Debug;

namespace CrocChamp2013QRE {
    public class Program {
        public class Reader {
            private TextReader reader;
            private Queue<string> tokens;

            public Reader(Stream s) {
                reader = new StreamReader(s);
                tokens = new Queue<string>();
            }

            public Reader(string file) {
                reader = new StreamReader(file);
                tokens = new Queue<string>();
            }

            public string ReadLine() { return reader.ReadLine(); }

            public string ReadToken() {
                while (tokens.Count() == 0) tokens = new Queue<string>(ReadLine().Split(new[] { ' ', '\t', }, StringSplitOptions.RemoveEmptyEntries));
                return tokens.Dequeue();
            }

            public char ReadChar() { return ReadToken()[0]; }
            public sbyte ReadByte() { return sbyte.Parse(ReadToken()); }
            public short ReadShort() { return short.Parse(ReadToken()); }
            public int ReadInt() { return int.Parse(ReadToken()); }
            public long ReadLong() { return long.Parse(ReadToken()); }
            public float ReadFloat() { return float.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public double ReadDouble() { return double.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public decimal ReadDecimal() { return decimal.Parse(ReadToken(), CultureInfo.InvariantCulture); }
            public void Close() { reader.Close(); }
        }

        private static Reader In;
        private static TextWriter Out;

        private static int NUM_OF_TEST_CASES = 1; // TODO CHANGE NUMBER OF TEST CASES

        // TODO CHANGE FILE NAMES
        private static string INPUT_FILE_NAME = "input.txt";
        private static string OUTPUT_FILE_NAME = "output.txt";

        private static bool stdIn = true;
        private static bool stdOut = true;
        private static bool crash = true;
        private static bool flush = false;

        private static int MEMORY_LIMIT_MB = 150;

        public static void Main(string[] args) {
            Thread T = new Thread(new ThreadStart(start), MEMORY_LIMIT_MB * 1000000);
            T.Start();
        }

        public static void start() {
            if (stdIn) In = new Reader(Console.OpenStandardInput());
            else In = new Reader(INPUT_FILE_NAME);
            if (stdOut) Out = new StreamWriter(Console.OpenStandardOutput());
            else Out = new StreamWriter(OUTPUT_FILE_NAME);
            for (int i = 1; i <= NUM_OF_TEST_CASES; i++) {
                try {
                    run(i);
                } catch (Exception e) {
                    Out.WriteLine("Exception thrown on test case " + i);
                    Out.WriteLine(e.StackTrace);
                    Out.Flush();
                    if (crash) throw new Exception();
                }
                if (flush) Out.Flush();
            }
            In.Close();
            Out.Close();
        }

        static int N;
        static int[] LCP;
        static string[] S;
        static string T;
        static List<int>[] adj;

        static int dfs(int v, int j) {
            int ret = 0;
            for (int i = 0; i < S[v].Length; i++, j++) {
                while (j >= 0 && (j == T.Length || S[v][i] != T[j])) j = LCP[j];
                if (j == T.Length - 1) ret++;
            }
            foreach (int w in adj[v]) ret += dfs(w, j);
            return ret;
        }

        // TODO CODE GOES IN THIS METHOD
        public static void run(int testCaseNum) {
            N = In.ReadInt();
            adj = new List<int>[N];
            S = new string[N];
            for (int i = 0; i < N; i++) adj[i] = new List<int>();
            S[0] = "";
            for (int i = 1; i < N; i++) {
                adj[In.ReadInt() - 1].Add(i);
                S[i] = In.ReadToken();
            }
            T = In.ReadToken();
            LCP = new int[T.Length + 1];
            LCP[0] = -1;
            for (int i = 0, j = -1; i < T.Length; i++, j++, LCP[i] = (j == T.Length || T[i] == T[j]) ? LCP[j] : j) {
                while (j >= 0 && T[i] != T[j]) j = LCP[j];
            }
            Out.WriteLine(dfs(0, 0));
        }
    }
}


#include<bits/stdc++.h>

#define sc second
#define fr first

#define ll long long
#define pi pair<int,int>
#define ppi pair<pi,pi>
#define mp make_pair
using namespace std;

int seg[200020 * 8][2];
int Lazy[200020 * 8][2];
void update(int i, int j, int val, int L, int R, int t, int p = 1)
{
	seg[p][t] = max(seg[p][t], Lazy[p][t]);
	Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t], Lazy[p][t]);
	Lazy[p * 2][t] = max(Lazy[p * 2][t], Lazy[p][t]);

	Lazy[p][t] = 0;

	if (R < L)return;
	if (L >= i && R <= j)
	{
		seg[p][t] = max(seg[p][t], val);
		Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t], val);
		Lazy[p * 2][t] = max(Lazy[p * 2][t], val);
		return;
	}
	if (L > j || R < i)
		return;
	int Mid = L + (R - L) / 2;
	update(i, j, val, L, Mid, t, p * 2);
	update(i, j, val, Mid + 1, R, t, p * 2 + 1);
	seg[p][t] = max(seg[p * 2][t], seg[p * 2 + 1][t]);
}
int quary(int i, int L, int R, int t, int p = 1)
{
	if (Lazy[p][t])
		seg[p][t] = max(seg[p][t], Lazy[p][t]);
	Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t], Lazy[p][t]);
	Lazy[p * 2][t] = max(Lazy[p * 2][t], Lazy[p][t]);

	Lazy[p][t] = 0;

	if (R < L)return 0;
	if (L > i || R < i)return 0;
	if (L == i && R == i)
		return seg[p][t];
	int Mid = L + (R - L) / 2;
	int C1 = quary(i, L, Mid, t, p * 2);
	int C2 = quary(i, Mid + 1, R, t, p * 2 + 1);
	return max(C1, C2);
}

int main()
{

	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int n; cin >> n;
	int q; cin >> q;

	map<int, int>m1, m2;
	vector<pair<pair<int, int>, char> >v;
	for (int i = 1; i <= q; i++)
	{
		int x, y; cin >> x >> y;
		char c; cin >> c;
		swap(x, y);
		v.push_back(mp(mp(x, y), c));
		m1[x];
		m2[y];
	}
	int cnt1 = 1;
	for (auto& i : m1)
		i.sc = cnt1++;
	int cnt2 = 1;
	for (auto& i : m2)
		i.sc = cnt2++;
	map<pair<int, int>, int>vis;
	cnt1--; cnt2--;
	for (int i = 0; i < v.size(); i++)
	{
		int x = v[i].fr.fr, y = v[i].fr.sc;
		char c = v[i].sc;
		int invx = m1[v[i].fr.fr];
		int invy = m2[v[i].fr.sc];
		if (c == 'U') {
			int tmp = quary(invy, 1, cnt2, 1);
			if (vis[v[i].fr])
				cout << 0 << '\n';
			else
				cout << x - tmp << '\n';
			vis[v[i].fr] = 1;
			map<int, int>::iterator it = m1.lower_bound(tmp + 1);
			int tt = -1;
			if (it == m1.end())tt = cnt1;
			else
				tt = it->second;
			update(tt, invx, y, 1, cnt1, 0);
			update(invy, invy, x, 1, cnt2, 1);
		}
		else {
			int tmp = quary(invx, 1, cnt1, 0);
			if (vis[v[i].fr])
				cout << 0 << '\n';
			else
				cout << y - tmp << '\n';
			vis[v[i].fr] = 1;
			map<int, int>::iterator it = m2.lower_bound(tmp + 1);
			int tt = -1;
			if (it == m2.end())tt = cnt2;
			else
				tt = it->second;
			update(tt, invy, x, 1, cnt2, 1);
			update(invx, invx, y, 1, cnt1, 0);
		}
	}

	return 0;
}


#include<bits/stdc++.h>

#define sc second
#define fr first

#define ll long long
#define pi pair<int,int>
#define ppi pair<pi,pi>
#define mp make_pair
using namespace std;

int seg[200020 * 8][2];
int Lazy[200020 * 8][2];

void push(int p,bool t)
{
	if (Lazy[p][t])
		seg[p][t] = max(seg[p][t], Lazy[p][t])
		, Lazy[p * 2][t] = Lazy[p * 2 + 1][t] = Lazy[p][t]
		, Lazy[p][t] = 0;
}
void update(int i, int j, int val, int L, int R, int t,int p = 1)
{
	if (R < L)return;
	push(p,t);
	if (L >= i && R <= j)
	{
		Lazy[p][t] = val;
		push(p,t);
		return;
	}
	if (L > j || R < i)
		return;
	int Mid = L + (R - L) / 2;
	update(i, j, val, L, Mid,t, p * 2);
	update(i, j, val, Mid + 1, R,t, p * 2  + 1);
	//seg[p] = max(seg[p * 2], seg[p * 2 + 1]);
}
int quary(int i, int L, int R,int t, int p = 1)
{
	if (L > i || R < i)return 0;
	push(p,t);
	if (R < L)return 0;
	if (L >= i && R <= i)
		return seg[p][t];
	int Mid = L + (R - L) / 2;
	int C1 = quary(i, L, Mid,t, p * 2);
	int C2 = quary(i, Mid + 1, R,t, p * 2 + 1);
	return max(C1, C2);
}

int main()
{

	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int n; cin >> n;
	int q; cin >> q;

	map<int, int>m1, m2;
	vector<pair<pair<int, int>, char> >v;
	for (int i = 1; i <= q; i++)
	{
		int x, y; cin >> x >> y;
		char c; cin >> c;
		swap(x, y);
		v.push_back(mp(mp(x, y), c));
		m1[x];
		m2[y];
	}
	int cnt1 = 1;
	for (auto& i : m1)
		i.sc = cnt1++;
	int cnt2 = 1;
	for (auto& i : m2)
		i.sc = cnt2++;

	cnt1--; cnt2--;
	for (int i = 0; i < v.size(); i++)
	{
		int x = v[i].fr.fr, y = v[i].fr.sc;
		char c = v[i].sc;
		int invx = m1[v[i].fr.fr];
		int invy = m2[v[i].fr.sc];
		if (c == 'U'){
			int tmp = quary(invy, 1, cnt2, 1);
			cout << x - tmp << '\n';
			map<int,int>::iterator it = m1.lower_bound(tmp);
			int tt = -1;
			if(it == m1.end())tt = cnt1;
			else
                tt = it->first;
			update(tt , invx , y, 1, cnt1, 0);
			update(invy, invy, x, 1, cnt2, 1);
		}
		else {
			int tmp = quary(x, 1, cnt1, 0);
			cout << y - tmp << '\n';
			map<int,int>::iterator it = m2.lower_bound(tmp );
			int tt = -1;
			if(it == m2.end())tt = cnt2;
			else
                tt = it->first;
			update(tt, y, x, 1, cnt2, 1);
			update(invx, invx, y, 1, cnt1, 0);
		}
	}

	return 0;
}


#include<bits/stdc++.h>

#define sc second
#define fr first

#define ll long long
#define pi pair<int,int>
#define ppi pair<pi,pi>
#define mp make_pair
using namespace std;

int seg[200020 * 8][2];
int Lazy[200020 * 8][2];
void update(int i, int j, int val, int L, int R, int t,int p = 1)
{
    seg[p][t] = max(seg[p][t],Lazy[p][t]);
    Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , Lazy[p][t]);
    Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , Lazy[p][t]);

    Lazy[p][t] = 0;

    if (R < L)return;
	if (L >= i && R <= j)
	{
	    seg[p][t] = max(seg[p][t] , val);
        Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , val);
        Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , val);
		return;
	}
	if (L > j || R < i)
		return;
	int Mid = L + (R - L) / 2;
	update(i, j, val, L, Mid,t, p * 2);
	update(i, j, val, Mid + 1, R,t, p * 2  + 1);
	seg[p][t] = max(seg[p * 2][t], seg[p * 2 + 1][t]);
}
int quary(int i, int L, int R,int t, int p = 1)
{
    if (Lazy[p][t])
        seg[p][t] = max(seg[p][t],Lazy[p][t]);
    Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , Lazy[p][t]);
    Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , Lazy[p][t]);

    Lazy[p][t] = 0;

	if (R < L)return 0;
	if (L > i || R < i)return 0;
	if(L == i && R == i)
        return seg[p][t];
    int Mid = L + (R - L) / 2;
	int C1 = quary(i, L, Mid,t, p * 2);
	int C2 = quary(i, Mid + 1, R,t, p * 2 + 1);
	return max(C1, C2);
}

int main()
{

	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int n; cin >> n;
	int q; cin >> q;

	map<int, int>m1, m2;
	vector<pair<pair<int, int>, char> >v;
	for (int i = 1; i <= q; i++)
	{
		int x, y; cin >> x >> y;
		char c; cin >> c;
		swap(x, y);
		v.push_back(mp(mp(x, y), c));
		m1[x];
		m2[y];
	}
	int cnt1 = 1;
	for (auto& i : m1)
		i.sc = cnt1++;
	int cnt2 = 1;
	for (auto& i : m2)
		i.sc = cnt2++;
    map<pair<int,int>,int>vis;
	cnt1--; cnt2--;
	for (int i = 0; i < v.size(); i++)
	{
		int x = v[i].fr.fr, y = v[i].fr.sc;
		char c = v[i].sc;
		int invx = m1[v[i].fr.fr];
		int invy = m2[v[i].fr.sc];
		if (c == 'U'){
			int tmp = quary(invy, 1, cnt2, 1);
			if(vis[v[i].fr])
                cout << 0 << '\n';
            else
                cout << x - tmp << '\n';
            vis[v[i].fr] = 1;
			map<int,int>::iterator it = m1.lower_bound(tmp + 1);
			int tt = -1;
			if(it == m1.end())tt = cnt1;
			else
                tt = it->second;
			update(tt , invx , y, 1, cnt1, 0);
			update(invy, invy, x, 1, cnt2, 1);
		}
		else {
			int tmp = quary(x, 1, cnt1, 0);
			if(vis[v[i].fr])
                cout << 0 << '\n';
            else
                cout << y - tmp << '\n';
            vis[v[i].fr] = 1;
			map<int,int>::iterator it = m2.lower_bound(tmp + 1);
			int tt = -1;
			if(it == m2.end())tt = cnt2;
			else
                tt = it->second;
			update(tt, invy, x, 1, cnt2, 1);
			update(invx, invx, y, 1, cnt1, 0);
		}
	}

	return 0;
}


#include<bits/stdc++.h>

#define sc second
#define fr first

#define ll long long
#define pi pair<int,int>
#define ppi pair<pi,pi>
#define mp make_pair
using namespace std;

int seg[200020 * 8][2];
int Lazy[200020 * 8][2];
void update(int i, int j, int val, int L, int R, int t,int p = 1)
{
    seg[p][t] = max(seg[p][t],Lazy[p][t]);
    Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , Lazy[p][t]);
    Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , Lazy[p][t]);

    Lazy[p][t] = 0;

    if (R < L)return;
	if (L >= i && R <= j)
	{
	    seg[p][t] = max(seg[p][t] , val);
        Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , val);
        Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , val);
		return;
	}
	if (L > j || R < i)
		return;
	int Mid = L + (R - L) / 2;
	update(i, j, val, L, Mid,t, p * 2);
	update(i, j, val, Mid + 1, R,t, p * 2  + 1);
	seg[p][t] = max(seg[p * 2][t], seg[p * 2 + 1][t]);
}
int quary(int i, int L, int R,int t, int p = 1)
{
    if (Lazy[p][t])
        seg[p][t] = max(seg[p][t],Lazy[p][t]);
    Lazy[p * 2 + 1][t] = max(Lazy[p * 2 + 1][t] , Lazy[p][t]);
    Lazy[p * 2 ][t] = max(Lazy[p * 2 ][t] , Lazy[p][t]);

    Lazy[p][t] = 0;

	if (R < L)return 0;
	if (L > i || R < i)return 0;
	if(L == i && R == i)
        return seg[p][t];
    int Mid = L + (R - L) / 2;
	int C1 = quary(i, L, Mid,t, p * 2);
	int C2 = quary(i, Mid + 1, R,t, p * 2 + 1);
	return max(C1, C2);
}

int main()
{

	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int n; cin >> n;
	int q; cin >> q;

	map<int, int>m1, m2;
	vector<pair<pair<int, int>, char> >v;
	for (int i = 1; i <= q; i++)
	{
		int x, y; cin >> x >> y;
		char c; cin >> c;
		swap(x, y);
		v.push_back(mp(mp(x, y), c));
		m1[x];
		m2[y];
	}
	int cnt1 = 1;
	for (auto& i : m1)
		i.sc = cnt1++;
	int cnt2 = 1;
	for (auto& i : m2)
		i.sc = cnt2++;
    map<pair<int,int>,int>vis;
	cnt1--; cnt2--;
	for (int i = 0; i < v.size(); i++)
	{
		int x = v[i].fr.fr, y = v[i].fr.sc;
		char c = v[i].sc;
		int invx = m1[v[i].fr.fr];
		int invy = m2[v[i].fr.sc];
		if (c == 'U'){
			int tmp = quary(invy, 1, cnt2, 1);
			if(vis[v[i].fr])
                cout << 0 << '\n';
            else
                cout << x - tmp << '\n';
            vis[v[i].fr] = 1;
			map<int,int>::iterator it = m1.lower_bound(tmp );
			int tt = -1;
			if(it == m1.end())tt = cnt1;
			else
                tt = it->first;
			update(tt , invx , y, 1, cnt1, 0);
			update(invy, invy, x, 1, cnt2, 1);
		}
		else {
			int tmp = quary(x, 1, cnt1, 0);
			if(vis[v[i].fr])
                cout << 0 << '\n';
            else
                cout << y - tmp << '\n';
            vis[v[i].fr] = 1;
			map<int,int>::iterator it = m2.lower_bound(tmp );
			int tt = -1;
			if(it == m2.end())tt = cnt2;
			else
                tt = it->first;
			update(tt, invy, x, 1, cnt2, 1);
			update(invx, invx, y, 1, cnt1, 0);
		}
	}

	return 0;
}


#include <bits/stdc++.h>
#define loop(i,n) for(int i = 0;i < (n);i++)
#define range(i,a,b) for(int i = (a);i <= (b);i++)
#define all(A) A.begin(),A.end()
#define PI acos(-1)
#define pb push_back
#define mp make_pair
#define sz(A) ((int)A.size())
#define vi vector<int>
#define vl vector<long long>
#define vd vector<double>
#define vp vector<pair<int,int> >
#define ll long long
#define pi pair<int,int>
#define point pair<double,double>
#define pl pair<ll,ll>
#define popcnt(x) __builtin_popcount(x)
#define LSOne(x) ((x) & (-(x)))
#define xx first
#define yy second
#define PQ priority_queue
#define print(A,t) cerr << #A << ": "; copy(all(A),ostream_iterator<t>(cerr," " )); cerr << endl
#define prp(p) cerr << "(" << (p).first << " ," << (p).second << ")";
#define prArr(A,n,t)  cerr << #A << ": "; copy(A,A + n,ostream_iterator<t>(cerr," " )); cerr << endl
#define PRESTDIO() cin.tie(0),cerr.tie(0),ios_base::sync_with_stdio(0)
using namespace std;

const int MAX = 2e5 + 1e2;
int n,m;
int X[MAX],Y[MAX],F[MAX],G[MAX],k1,k2;
char type[MAX];
int ST[2][MAX << 2];

void compress(int *X,int *F,int & k){
	sort(F,F + m + 1);
	k = unique(F,F + m + 1) - F;
	loop(i,m) X[i] = lower_bound(F,F + k,X[i]) - F;
}

void update(int node,int s,int e,int p,int v,int *T){
	if(e <= p){
		T[node] = max(T[node],v);
		return;
	}
	int m = (s + e) >> 1,left = node << 1,right = left | 1;
	update(left,s,m,p,v,T);
	if(m < p) update(right,m+1,e,p,v,T);
}

int query(int node,int s,int e,int p,int *T){
	if(s == e) return T[node];
	int m = (s + e) >> 1,left = node << 1,right = left | 1;
	if(p <= m) return max(T[node],query(left,s,m,p,T));
	else return max(T[node],query(right,m+1,e,p,T));
}

int main(){
	scanf("%d %d",&n,&m);
	loop(i,m){
		scanf("%d %d %c",X + i,Y + i,&type[i]);
		swap(X[i],Y[i]);
		F[i] = X[i];
		G[i] = Y[i];
	}
	compress(X,F,k1);
	compress(Y,G,k2);
	loop(i,m){
		//cerr << X[i] << " " << Y[i] << endl;
		if(type[i] == 'U'){
			int r = query(1,0,k2 - 1,Y[i],ST[0]);
		//	cerr << "U " << F[X[i]] << " -> " << G[Y[i]] << " -> " << F[r] << endl;
			printf("%d\n",F[X[i]] - F[r]);
			update(1,0,k1 - 1,X[i],Y[i],ST[1]);
		}
		else{
			int c = query(1,0,k1-1,X[i],ST[1]);
		//	cerr << "L " << F[X[i]] << " -> " << G[c] << endl;
			printf("%d\n",G[Y[i]] - G[c]);
			update(1,0,k2 - 1,Y[i],X[i],ST[0]);
		}
	}
	return 0;
}


#include <bits/stdc++.h>
#define loop(i,n) for(int i = 0;i < (n);i++)
#define range(i,a,b) for(int i = (a);i <= (b);i++)
#define all(A) A.begin(),A.end()
#define PI acos(-1)
#define pb push_back
#define mp make_pair
#define sz(A) ((int)A.size())
#define vi vector<int>
#define vl vector<long long>
#define vd vector<double>
#define vp vector<pair<int,int> >
#define ll long long
#define pi pair<int,int>
#define point pair<double,double>
#define pl pair<ll,ll>
#define popcnt(x) __builtin_popcount(x)
#define LSOne(x) ((x) & (-(x)))
#define xx first
#define yy second
#define PQ priority_queue
#define print(A,t) cerr << #A << ": "; copy(all(A),ostream_iterator<t>(cerr," " )); cerr << endl
#define prp(p) cerr << "(" << (p).first << " ," << (p).second << ")";
#define prArr(A,n,t)  cerr << #A << ": "; copy(A,A + n,ostream_iterator<t>(cerr," " )); cerr << endl
#define PRESTDIO() cin.tie(0),cerr.tie(0),ios_base::sync_with_stdio(0)
using namespace std;

const int MAX = 2e5 + 1e2;
int n,m;
int X[MAX],Y[MAX],F[MAX],G[MAX],k1,k2;
char type[MAX];
int ST[2][MAX << 2];
set<int> S;

void compress(int *X,int *F,int & k){
	sort(F,F + m + 1);
	k = unique(F,F + m + 1) - F;
	loop(i,m) X[i] = lower_bound(F,F + k,X[i]) - F;
}

void update(int node,int s,int e,int p,int v,int *T){
	if(e <= p){
		T[node] = max(T[node],v);
		return;
	}
	int m = (s + e) >> 1,left = node << 1,right = left | 1;
	update(left,s,m,p,v,T);
	if(m < p) update(right,m+1,e,p,v,T);
}

int query(int node,int s,int e,int p,int *T){
	if(s == e) return T[node];
	int m = (s + e) >> 1,left = node << 1,right = left | 1;
	if(p <= m) return max(T[node],query(left,s,m,p,T));
	else return max(T[node],query(right,m+1,e,p,T));
}

int main(){
	scanf("%d %d",&n,&m);
	loop(i,m){
		scanf("%d %d %c",X + i,Y + i,&type[i]);
		swap(X[i],Y[i]);
		F[i] = X[i];
		G[i] = Y[i];
	}
	compress(X,F,k1);
	compress(Y,G,k2);
	loop(i,m){
		//cerr << X[i] << " " << Y[i] << endl;
		if(S.find(X[i]) != S.end()){
			puts("0");
			continue;
		}
		if(type[i] == 'U'){
			int r = query(1,0,k2 - 1,Y[i],ST[0]);
		//	cerr << "U " << F[X[i]] << " -> " << G[Y[i]] << " -> " << F[r] << endl;
			printf("%d\n",F[X[i]] - F[r]);
			update(1,0,k1 - 1,X[i],Y[i],ST[1]);
		}
		else{
			int c = query(1,0,k1-1,X[i],ST[1]);
		//	cerr << "L " << F[X[i]] << " -> " << G[c] << endl;
			printf("%d\n",G[Y[i]] - G[c]);
			update(1,0,k2 - 1,Y[i],X[i],ST[0]);
		}
		S.insert(X[i]);
	}
	return 0;
}