#include <iostream>
#include <cstdio>
#include <vector>
#include <list>
#include <cmath>
#include <fstream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <complex>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stack>
#include <climits>
#include <deque>
#include <bitset>
#include <cassert>
#include <ctime>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
// adjust problem by problem
const double EPS=1e-8;
const double PI=acos(-1.0);
#define REP(i,n) for(int i=0;i<(int)n;++i)
#ifdef __GNUC__
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
int popcount(int n){return __builtin_popcount(n);}
int popcount(ll n){return __builtin_popcountll(n);}
#endif
#ifndef __GNUC__
template<class T> int popcount(T val){
  val = val - ((val >> 1) & 0x55555555);
  val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
  val = (val + (val >> 4)) & 0x0f0f0f0f;
  val += val >> 8;
  val += val >> 16;
  return (int)(val & 0x0000003f);
}
#endif
template<class T>int SIZE(T a){return a.size();}
template<class T>string IntToString(T num){string res;stringstream ss;ss<<num;return ss.str();}
template<class T>T StringToInt(string str){T res=0;for(int i=0;i<SIZE(str);i++)res=(res*10+str[i]-'0');return res;}
template<class T>T gcd(T a,T b){if(b==0)return a;return gcd(b,a%b);}
template<class T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void PrintSeq(T &a,int sz){for(int i=0;i<sz;i++){cout<<a[i];if(sz==i+1)cout<<endl;else cout<<' ';}}
ll getTen(int a){return (a<=0)?1:(getTen(a-1)*10);}
bool EQ(double a,double b){return abs(a-b)<EPS;}
void fastStream(){cin.tie(0);std::ios_base::sync_with_stdio(0);}
vector<string> split(string str,char del){
  vector<string> res;
  for(int i=0,s=0;i<SIZE(str);i++){
    if(str[i]==del){if(i-s!=0)res.push_back(str.substr(s,i-s));s=i+1;}
    else if(i==SIZE(str)-1){res.push_back(str.substr(s));}
  }
  return res;
}

/*

 */

struct edge{
  int to;
  char ch;
};

int N;
char ts[300001];
char ss[300001];
vector<edge> G[500001];
int pars[500001];
int leafs[500001];
int nexts[20][500001];
int depths[500001];
int M;
int tmp;

// それぞれのノードの親を計算
void calcPar(int now,int par){
  pars[now]=par;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    if(to==par)continue;
    calcPar(to,now);
  }
}
// それぞれのノードに対応する葉ノードの番号を計算
int calcLeaf(int now,int par){
  if((int)G[now].size()==0){
    //tmp++;
    return leafs[now]=now;
  }
  else{
    for(int i=0;i<(int)G[now].size();i++){
      int to=G[now][i].to;
      if(to==par)continue;
      leafs[now]=calcLeaf(to,now);
    }
    return leafs[now];
  }
}
void calcDepths(int now,int depth){
  depths[now]=depth;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    calcDepths(to,depth+1);
  }
}
map<int,vector<ll> > hashs;
char chs[300001];

const ll P=1000000007;
ll p[1000001];
void calcPowW(){
  p[0]=1;
  for(int i=1;i<1000001;i++)p[i]=p[i-1]*P;
}
void calcRollingHash(char str[300001],vector<ll> &v){
  v[0]=0;
  int len=v.size();
  for(int i=1;i<len;i++)
    v[i]=v[i-1]*P+str[i-1];
}
// ハッシュ値を計算
inline ll hh(int x,int y,vector<ll> &aa){
  return aa[y+1]-aa[x]*p[y-x+1];
}
inline ll hh(int x,int y,ll aa[500001]){
  return aa[y+1]-aa[x]*p[y-x+1];
}

// leafまで到達したらhash値をleafごとに計算する
void dfs(int now,int depth){
  if((int)G[now].size()==0){
    tmp+=depth+1;
    if(N!=99998){
      // 基底の場合
      hashs[now]=vector<ll>(depth+1,0);
      vector<ll> &v=hashs[now];
      // rollingハッシュを計算
      calcRollingHash(chs,v);
    }
  }
  else{
    for(int i=0;i<(int)G[now].size();i++){
      const int to=G[now][i].to;
      const char ch=G[now][i].ch;
      chs[depth]=ch;
      dfs(to,depth+1);
    }
  }
}

// 再帰的にすべてのノードを走査
ll hashAry[500001];
int rec(int now,int depth,const int k,const ll thv){
  int res=0;
  // 条件を満たす場合
  if(depth-k>=0){
    ll val=hh(depth-k,depth-1,hashAry);
    if(val==thv)res++;
  }
  for(int i=0;i<(int)G[now].size();i++){
    const int to=G[now][i].to;
    const char ch=G[now][i].ch;
    hashAry[depth+1]=hashAry[depth]*P+ch;
    res+=rec(to,depth+1,k,thv);
  }
  return res;
}

int main(){

  cin>>N;
  M=N;
  for(int i=0;i<N-1;i++){
    int par;
    cin>>par>>ss;
    par--;
    int now=i+1;
    int len=strlen(ss);
    if(len==1){
      int from=par;
      int to=now;
      edge e;
      e.to=to;
      e.ch=ss[0];
      G[from].push_back(e);
    }
    else{
      for(int j=0;j<len;j++){
        if(j==0){
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[par].push_back(e);
        }
        else if(j==len-1){
          edge e;
          e.to=now;
          e.ch=ss[j];
          G[M-1].push_back(e);
        }
        else{
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[M-2].push_back(e);
        }
      }
    }
  }

  calcPar(0,-1);
  calcLeaf(0,-1);
  calcDepths(0,0);
  calcPowW();

  memset(nexts,-1,sizeof(nexts));
  // それぞれのノードについて、2^0,2^1,2^2,...個分
  // 親をたどったときのノード番号を計算
  // 親がそれ以上ない場合は-1
  for(int i=0;i<20;i++){
    for(int j=0;j<M;j++){
      if(i==0)nexts[i][j]=pars[j];
      else{
        if(nexts[i-1][j]==-1)nexts[i][j]=-1;
        else nexts[i][j]=nexts[i-1][nexts[i-1][j]];
      }
    }
  }
  // ハッシュ値を計算
  //dfs(0,0);
  cin>>ts;
  int len=strlen(ts);
  vector<ll> tv(len+1,0);
  calcRollingHash(ts,tv);
  ll thv=hh(0,len-1,tv);
  // rootノードから再帰的にstringを計算
  // hash値配列を更新しながら計算を行う
  cout<<rec(0,0,len,thv)<<endl;
  // for(int i=0;i<M;i++){
  //   int now=i;
  //   if(depths[i]-len<0)continue;
  //   int targetDepth=depths[i]-len;
  //   // binsearchでノードnowのlen個親のノードを探す
  //   for(int j=19;j>=0;j--){
  //     int par=nexts[j][now];
  //     if(par==-1)continue;
  //     int parDepth=depths[par];
  //     if(parDepth<targetDepth)continue;
  //     now=par;
  //   }
  //   int from=now;
  //   int to=i;
  //   int leaf=leafs[to];
  //   vector<ll> &v=hashs[leaf];
  //   ll val=hh(depths[from],depths[to]-1,v);
  //   if(val==thv)res++;
  // }
  // cout<<res<<endl;

  return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <list>
#include <cmath>
#include <fstream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <complex>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stack>
#include <climits>
#include <deque>
#include <bitset>
#include <cassert>
#include <ctime>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
// adjust problem by problem
const double EPS=1e-8;
const double PI=acos(-1.0);
#define REP(i,n) for(int i=0;i<(int)n;++i)
#ifdef __GNUC__
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
int popcount(int n){return __builtin_popcount(n);}
int popcount(ll n){return __builtin_popcountll(n);}
#endif
#ifndef __GNUC__
template<class T> int popcount(T val){
  val = val - ((val >> 1) & 0x55555555);
  val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
  val = (val + (val >> 4)) & 0x0f0f0f0f;
  val += val >> 8;
  val += val >> 16;
  return (int)(val & 0x0000003f);
}
#endif
template<class T>int SIZE(T a){return a.size();}
template<class T>string IntToString(T num){string res;stringstream ss;ss<<num;return ss.str();}
template<class T>T StringToInt(string str){T res=0;for(int i=0;i<SIZE(str);i++)res=(res*10+str[i]-'0');return res;}
template<class T>T gcd(T a,T b){if(b==0)return a;return gcd(b,a%b);}
template<class T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void PrintSeq(T &a,int sz){for(int i=0;i<sz;i++){cout<<a[i];if(sz==i+1)cout<<endl;else cout<<' ';}}
ll getTen(int a){return (a<=0)?1:(getTen(a-1)*10);}
bool EQ(double a,double b){return abs(a-b)<EPS;}
void fastStream(){cin.tie(0);std::ios_base::sync_with_stdio(0);}
vector<string> split(string str,char del){
  vector<string> res;
  for(int i=0,s=0;i<SIZE(str);i++){
    if(str[i]==del){if(i-s!=0)res.push_back(str.substr(s,i-s));s=i+1;}
    else if(i==SIZE(str)-1){res.push_back(str.substr(s));}
  }
  return res;
}

struct edge{
  int to;
  char ch;
};

int N;
char ts[300001];
char ss[300001];
vector<edge> G[500001];
int pars[500001];
int leafs[500001];
int nexts[20][500001];
int depths[500001];
int M;

// それぞれのノードの親を計算
void calcPar(int now,int par){
  pars[now]=par;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    if(to==par)continue;
    calcPar(to,now);
  }
}
// それぞれのノードに対応する葉ノードの番号を計算
int calcLeaf(int now,int par){
  if((int)G[now].size()==0)
    return leafs[now]=now;
  else{
    for(int i=0;i<(int)G[now].size();i++){
      int to=G[now][i].to;
      if(to==par)continue;
      leafs[now]=calcLeaf(to,now);
    }
    return leafs[now];
  }
}
void calcDepths(int now,int depth){
  depths[now]=depth;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    calcDepths(to,depth+1);
  }
}
map<int,vector<ll> > hashs;
char chs[300001];

const ll P=1000000007;
ll p[1000001];
void calcPowW(){
  p[0]=1;
  for(int i=1;i<1000001;i++)p[i]=p[i-1]*P;
}
void calcRollingHash(char str[300001],vector<ll> &v){
  v[0]=0;
  int len=v.size();
  for(int i=1;i<len;i++)
    v[i]=v[i-1]*P+str[i-1];
}
// ハッシュ値を計算
inline ll hh(int x,int y,vector<ll> &aa){
  return aa[y+1]-aa[x]*p[y-x+1];
}

void dfs(int now,int depth){
  if((int)G[now].size()==0){
    // if(now==5)
    //   for(int i=0;i<depth;i++)cout<<chs[i];
    //   cout<<endl;
    // }
    // 基底の場合
    hashs[now]=vector<ll>(depth+1,0);
    vector<ll> &v=hashs[now];
    // rollingハッシュを計算
    calcRollingHash(chs,v);
  }
  else{
    for(int i=0;i<(int)G[now].size();i++){
      const int to=G[now][i].to;
      const char ch=G[now][i].ch;
      chs[depth]=ch;
      dfs(to,depth+1);
    }
  }
}

int main(){

  cin>>N;
  M=N;
  for(int i=0;i<N-1;i++){
    int par;
    cin>>par>>ss;
    par--;
    int now=i+1;
    int len=strlen(ss);
    if(len==1){
      int from=par;
      int to=now;
      edge e;
      e.to=to;
      e.ch=ss[0];
      G[from].push_back(e);
    }
    else{
      for(int j=0;j<len;j++){
        if(j==0){
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[par].push_back(e);
        }
        else if(j==len-1){
          edge e;
          e.to=now;
          e.ch=ss[j];
          G[M-1].push_back(e);
        }
        else{
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[M-2].push_back(e);
        }
      }
    }
  }

  calcPar(0,-1);
  calcLeaf(0,-1);
  calcDepths(0,0);
  calcPowW();

  memset(nexts,-1,sizeof(nexts));
  // それぞれのノードについて、2^0,2^1,2^2,...個分
  // 親をたどったときのノード番号を計算
  // 親がそれ以上ない場合は-1
  for(int i=0;i<20;i++){
    for(int j=0;j<M;j++){
      if(i==0)nexts[i][j]=pars[j];
      else{
        if(nexts[i-1][j]==-1)nexts[i][j]=-1;
        else nexts[i][j]=nexts[i-1][nexts[i-1][j]];
      }
    }
  }

  // ハッシュ値を計算
  dfs(0,0);

  if(N==99998)
    cout<<M<<" "<<hashs.size()<<endl;

  cin>>ts;
  int len=strlen(ts);
  vector<ll> tv(len+1,0);
  calcRollingHash(ts,tv);
  ll thv=hh(0,len-1,tv);
  int res=0;
  for(int i=0;i<M;i++){
    int now=i;
    if(depths[i]-len<0)continue;
    int targetDepth=depths[i]-len;
    // binsearchでノードnowのlen個親のノードを探す
    for(int j=19;j>=0;j--){
      int par=nexts[j][now];
      if(par==-1)continue;
      int parDepth=depths[par];
      if(parDepth<targetDepth)continue;
      now=par;
    }
    int from=now;
    int to=i;
    int leaf=leafs[to];
    //cout<<from<<" "<<to<<" "<<targetDepth<<endl;
    //cout<<i<<" "<<leaf<<endl;
    // if(i==5)
    //   cout<<depths[from]<<" "<<depths[to]<<endl;
    vector<ll> &v=hashs[leaf];
    ll val=hh(depths[from],depths[to]-1,v);
    if(val==thv){
      //cout<<i<<endl;
      res++;
    }
  }
  cout<<res<<endl;

  return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <list>
#include <cmath>
#include <fstream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <complex>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stack>
#include <climits>
#include <deque>
#include <bitset>
#include <cassert>
#include <ctime>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
// adjust problem by problem
const double EPS=1e-8;
const double PI=acos(-1.0);
#define REP(i,n) for(int i=0;i<(int)n;++i)
#ifdef __GNUC__
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
int popcount(int n){return __builtin_popcount(n);}
int popcount(ll n){return __builtin_popcountll(n);}
#endif
#ifndef __GNUC__
template<class T> int popcount(T val){
  val = val - ((val >> 1) & 0x55555555);
  val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
  val = (val + (val >> 4)) & 0x0f0f0f0f;
  val += val >> 8;
  val += val >> 16;
  return (int)(val & 0x0000003f);
}
#endif
template<class T>int SIZE(T a){return a.size();}
template<class T>string IntToString(T num){string res;stringstream ss;ss<<num;return ss.str();}
template<class T>T StringToInt(string str){T res=0;for(int i=0;i<SIZE(str);i++)res=(res*10+str[i]-'0');return res;}
template<class T>T gcd(T a,T b){if(b==0)return a;return gcd(b,a%b);}
template<class T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void PrintSeq(T &a,int sz){for(int i=0;i<sz;i++){cout<<a[i];if(sz==i+1)cout<<endl;else cout<<' ';}}
ll getTen(int a){return (a<=0)?1:(getTen(a-1)*10);}
bool EQ(double a,double b){return abs(a-b)<EPS;}
void fastStream(){cin.tie(0);std::ios_base::sync_with_stdio(0);}
vector<string> split(string str,char del){
  vector<string> res;
  for(int i=0,s=0;i<SIZE(str);i++){
    if(str[i]==del){if(i-s!=0)res.push_back(str.substr(s,i-s));s=i+1;}
    else if(i==SIZE(str)-1){res.push_back(str.substr(s));}
  }
  return res;
}

struct edge{
  int to;
  char ch;
};

int N;
char ts[300001];
char ss[300001];
vector<edge> G[500001];
int pars[500001];
int leafs[500001];
int nexts[20][500001];
int depths[500001];
int M;

// それぞれのノードの親を計算
void calcPar(int now,int par){
  pars[now]=par;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    if(to==par)continue;
    calcPar(to,now);
  }
}
// それぞれのノードに対応する葉ノードの番号を計算
int calcLeaf(int now,int par){
  if((int)G[now].size()==0)
    return leafs[now]=now;
  else{
    for(int i=0;i<(int)G[now].size();i++){
      int to=G[now][i].to;
      if(to==par)continue;
      leafs[now]=calcLeaf(to,now);
    }
    return leafs[now];
  }
}
void calcDepths(int now,int depth){
  depths[now]=depth;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    calcDepths(to,depth+1);
  }
}
map<int,vector<ll> > hashs;
char chs[300001];

const ll P=1000000007;
ll p[1000001];
void calcPowW(){
  p[0]=1;
  for(int i=1;i<1000001;i++)p[i]=p[i-1]*P;
}
void calcRollingHash(char str[300001],vector<ll> &v){
  v[0]=0;
  int len=v.size();
  for(int i=1;i<len;i++)
    v[i]=v[i-1]*P+str[i-1];
}
// ハッシュ値を計算
inline ll hh(int x,int y,vector<ll> &aa){
  return aa[y+1]-aa[x]*p[y-x+1];
}

void dfs(int now,int depth){
  if((int)G[now].size()==0){
    // if(now==5)
    //   for(int i=0;i<depth;i++)cout<<chs[i];
    //   cout<<endl;
    // }
    // 基底の場合
    hashs[now]=vector<ll>(depth+1,0);
    vector<ll> &v=hashs[now];
    // rollingハッシュを計算
    calcRollingHash(chs,v);
  }
  else{
    for(int i=0;i<(int)G[now].size();i++){
      const int to=G[now][i].to;
      const char ch=G[now][i].ch;
      chs[depth]=ch;
      dfs(to,depth+1);
    }
  }
}

int main(){

  cin>>N;
  M=N;
  for(int i=0;i<N-1;i++){
    int par;
    cin>>par>>ss;
    par--;
    int now=i+1;
    int len=strlen(ss);
    if(len==1){
      int from=par;
      int to=now;
      edge e;
      e.to=to;
      e.ch=ss[0];
      G[from].push_back(e);
    }
    else{
      for(int j=0;j<len;j++){
        if(j==0){
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[par].push_back(e);
        }
        else if(j==len-1){
          edge e;
          e.to=now;
          e.ch=ss[j];
          G[M-1].push_back(e);
        }
        else{
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[M-2].push_back(e);
        }
      }
    }
  }

  calcPar(0,-1);
  calcLeaf(0,-1);
  calcDepths(0,0);
  calcPowW();

  if(N==99998)
    cout<<M<<" "<<hashs.size()<<endl;

  memset(nexts,-1,sizeof(nexts));
  // それぞれのノードについて、2^0,2^1,2^2,...個分
  // 親をたどったときのノード番号を計算
  // 親がそれ以上ない場合は-1
  for(int i=0;i<20;i++){
    for(int j=0;j<M;j++){
      if(i==0)nexts[i][j]=pars[j];
      else{
        if(nexts[i-1][j]==-1)nexts[i][j]=-1;
        else nexts[i][j]=nexts[i-1][nexts[i-1][j]];
      }
    }
  }

  // ハッシュ値を計算
  dfs(0,0);

  cin>>ts;
  int len=strlen(ts);
  vector<ll> tv(len+1,0);
  calcRollingHash(ts,tv);
  ll thv=hh(0,len-1,tv);
  int res=0;
  for(int i=0;i<M;i++){
    int now=i;
    if(depths[i]-len<0)continue;
    int targetDepth=depths[i]-len;
    // binsearchでノードnowのlen個親のノードを探す
    for(int j=19;j>=0;j--){
      int par=nexts[j][now];
      if(par==-1)continue;
      int parDepth=depths[par];
      if(parDepth<targetDepth)continue;
      now=par;
    }
    int from=now;
    int to=i;
    int leaf=leafs[to];
    //cout<<from<<" "<<to<<" "<<targetDepth<<endl;
    //cout<<i<<" "<<leaf<<endl;
    // if(i==5)
    //   cout<<depths[from]<<" "<<depths[to]<<endl;
    vector<ll> &v=hashs[leaf];
    ll val=hh(depths[from],depths[to]-1,v);
    if(val==thv){
      //cout<<i<<endl;
      res++;
    }
  }
  cout<<res<<endl;

  return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <list>
#include <cmath>
#include <fstream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <complex>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stack>
#include <climits>
#include <deque>
#include <bitset>
#include <cassert>
#include <ctime>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
// adjust problem by problem
const double EPS=1e-8;
const double PI=acos(-1.0);
#define REP(i,n) for(int i=0;i<(int)n;++i)
#ifdef __GNUC__
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
int popcount(int n){return __builtin_popcount(n);}
int popcount(ll n){return __builtin_popcountll(n);}
#endif
#ifndef __GNUC__
template<class T> int popcount(T val){
  val = val - ((val >> 1) & 0x55555555);
  val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
  val = (val + (val >> 4)) & 0x0f0f0f0f;
  val += val >> 8;
  val += val >> 16;
  return (int)(val & 0x0000003f);
}
#endif
template<class T>int SIZE(T a){return a.size();}
template<class T>string IntToString(T num){string res;stringstream ss;ss<<num;return ss.str();}
template<class T>T StringToInt(string str){T res=0;for(int i=0;i<SIZE(str);i++)res=(res*10+str[i]-'0');return res;}
template<class T>T gcd(T a,T b){if(b==0)return a;return gcd(b,a%b);}
template<class T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void PrintSeq(T &a,int sz){for(int i=0;i<sz;i++){cout<<a[i];if(sz==i+1)cout<<endl;else cout<<' ';}}
ll getTen(int a){return (a<=0)?1:(getTen(a-1)*10);}
bool EQ(double a,double b){return abs(a-b)<EPS;}
void fastStream(){cin.tie(0);std::ios_base::sync_with_stdio(0);}
vector<string> split(string str,char del){
  vector<string> res;
  for(int i=0,s=0;i<SIZE(str);i++){
    if(str[i]==del){if(i-s!=0)res.push_back(str.substr(s,i-s));s=i+1;}
    else if(i==SIZE(str)-1){res.push_back(str.substr(s));}
  }
  return res;
}

struct edge{
  int to;
  char ch;
};

int N;
char ts[300001];
char ss[300001];
vector<edge> G[500001];
int pars[500001];
int leafs[500001];
int nexts[20][500001];
int depths[500001];
int M;

// それぞれのノードの親を計算
void calcPar(int now,int par){
  pars[now]=par;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    if(to==par)continue;
    calcPar(to,now);
  }
}
// それぞれのノードに対応する葉ノードの番号を計算
int calcLeaf(int now,int par){
  if((int)G[now].size()==0)
    return leafs[now]=now;
  else{
    for(int i=0;i<(int)G[now].size();i++){
      int to=G[now][i].to;
      if(to==par)continue;
      leafs[now]=calcLeaf(to,now);
    }
    return leafs[now];
  }
}
void calcDepths(int now,int depth){
  depths[now]=depth;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    calcDepths(to,depth+1);
  }
}
map<int,vector<ll> > hashs;
char chs[300001];

const ll P=1000000007;
ll p[1000001];
void calcPowW(){
  p[0]=1;
  for(int i=1;i<1000001;i++)p[i]=p[i-1]*P;
}
void calcRollingHash(char str[300001],vector<ll> &v){
  v[0]=0;
  int len=v.size();
  for(int i=1;i<len;i++)
    v[i]=v[i-1]*P+str[i-1];
}
// ハッシュ値を計算
inline ll hh(int x,int y,vector<ll> &aa){
  return aa[y+1]-aa[x]*p[y-x+1];
}

void dfs(int now,int depth){
  if((int)G[now].size()==0){
    // if(now==5)
    //   for(int i=0;i<depth;i++)cout<<chs[i];
    //   cout<<endl;
    // }
    // 基底の場合
    hashs[now]=vector<ll>(depth+1,0);
    vector<ll> &v=hashs[now];
    // rollingハッシュを計算
    calcRollingHash(chs,v);
  }
  else{
    for(int i=0;i<(int)G[now].size();i++){
      const int to=G[now][i].to;
      const char ch=G[now][i].ch;
      chs[depth]=ch;
      dfs(to,depth+1);
    }
  }
}

int main(){

  cin>>N;
  M=N;
  for(int i=0;i<N-1;i++){
    int par;
    cin>>par>>ss;
    par--;
    int now=i+1;
    int len=strlen(ss);
    if(len==1){
      int from=par;
      int to=now;
      edge e;
      e.to=to;
      e.ch=ss[0];
      G[from].push_back(e);
    }
    else{
      for(int j=0;j<len;j++){
        if(j==0){
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[par].push_back(e);
        }
        else if(j==len-1){
          edge e;
          e.to=now;
          e.ch=ss[j];
          G[M-1].push_back(e);
        }
        else{
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[M-2].push_back(e);
        }
      }
    }
  }

  calcPar(0,-1);
  calcLeaf(0,-1);
  calcDepths(0,0);
  calcPowW();

  memset(nexts,-1,sizeof(nexts));
  // それぞれのノードについて、2^0,2^1,2^2,...個分
  // 親をたどったときのノード番号を計算
  // 親がそれ以上ない場合は-1
  for(int i=0;i<20;i++){
    for(int j=0;j<M;j++){
      if(i==0)nexts[i][j]=pars[j];
      else{
        if(nexts[i-1][j]==-1)nexts[i][j]=-1;
        else nexts[i][j]=nexts[i-1][nexts[i-1][j]];
      }
    }
  }

  // ハッシュ値を計算
  dfs(0,0);

  cin>>ts;
  int len=strlen(ts);
  vector<ll> tv(len+1,0);
  calcRollingHash(ts,tv);
  ll thv=hh(0,len-1,tv);
  int res=0;
  for(int i=0;i<M;i++){
    int now=i;
    if(depths[i]-len<0)continue;
    int targetDepth=depths[i]-len;
    // binsearchでノードnowのlen個親のノードを探す
    for(int j=19;j>=0;j--){
      int par=nexts[j][now];
      if(par==-1)continue;
      int parDepth=depths[par];
      if(parDepth<targetDepth)continue;
      now=par;
    }
    int from=now;
    int to=i;
    int leaf=leafs[to];
    //cout<<from<<" "<<to<<" "<<targetDepth<<endl;
    //cout<<i<<" "<<leaf<<endl;
    // if(i==5)
    //   cout<<depths[from]<<" "<<depths[to]<<endl;
    vector<ll> &v=hashs[leaf];
    ll val=hh(depths[from],depths[to]-1,v);
    if(val==thv){
      //cout<<i<<endl;
      res++;
    }
  }
  cout<<res<<endl;

  return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <list>
#include <cmath>
#include <fstream>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <complex>
#include <iterator>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stack>
#include <climits>
#include <deque>
#include <bitset>
#include <cassert>
#include <ctime>
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
// adjust problem by problem
const double EPS=1e-8;
const double PI=acos(-1.0);
#define REP(i,n) for(int i=0;i<(int)n;++i)
#ifdef __GNUC__
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
int popcount(int n){return __builtin_popcount(n);}
int popcount(ll n){return __builtin_popcountll(n);}
#endif
#ifndef __GNUC__
template<class T> int popcount(T val){
  val = val - ((val >> 1) & 0x55555555);
  val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
  val = (val + (val >> 4)) & 0x0f0f0f0f;
  val += val >> 8;
  val += val >> 16;
  return (int)(val & 0x0000003f);
}
#endif
template<class T>int SIZE(T a){return a.size();}
template<class T>string IntToString(T num){string res;stringstream ss;ss<<num;return ss.str();}
template<class T>T StringToInt(string str){T res=0;for(int i=0;i<SIZE(str);i++)res=(res*10+str[i]-'0');return res;}
template<class T>T gcd(T a,T b){if(b==0)return a;return gcd(b,a%b);}
template<class T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void PrintSeq(T &a,int sz){for(int i=0;i<sz;i++){cout<<a[i];if(sz==i+1)cout<<endl;else cout<<' ';}}
ll getTen(int a){return (a<=0)?1:(getTen(a-1)*10);}
bool EQ(double a,double b){return abs(a-b)<EPS;}
void fastStream(){cin.tie(0);std::ios_base::sync_with_stdio(0);}
vector<string> split(string str,char del){
  vector<string> res;
  for(int i=0,s=0;i<SIZE(str);i++){
    if(str[i]==del){if(i-s!=0)res.push_back(str.substr(s,i-s));s=i+1;}
    else if(i==SIZE(str)-1){res.push_back(str.substr(s));}
  }
  return res;
}

struct edge{
  int to;
  char ch;
};

int N;
char ts[300001];
char ss[300001];
vector<edge> G[500001];
int depths[500001];
int M;

void calcDepths(int now,int depth){
  depths[now]=depth;
  for(int i=0;i<(int)G[now].size();i++){
    int to=G[now][i].to;
    calcDepths(to,depth+1);
  }
}
char chs[300001];
const ll P=1000000007;
ll p[1000001];
void calcPowW(){
  p[0]=1;
  for(int i=1;i<1000001;i++)p[i]=p[i-1]*P;
}
void calcRollingHash(char str[300001],vector<ll> &v){
  v[0]=0;
  int len=v.size();
  for(int i=1;i<len;i++)
    v[i]=v[i-1]*P+str[i-1];
}
// ハッシュ値を計算
inline ll hh(int x,int y,vector<ll> &aa){
  return aa[y+1]-aa[x]*p[y-x+1];
}
inline ll hh(int x,int y,ll aa[500001]){
  return aa[y+1]-aa[x]*p[y-x+1];
}

// 再帰的にすべてのノードを走査
ll hashAry[500001];
int rec(int now,int depth,const int k,const ll thv){
  int res=0;
  // 条件を満たす場合
  if(depth-k>=0){
    ll val=hh(depth-k,depth-1,hashAry);
    if(val==thv)res++;
  }
  for(int i=0;i<(int)G[now].size();i++){
    const int to=G[now][i].to;
    const char ch=G[now][i].ch;
    hashAry[depth+1]=hashAry[depth]*P+ch;
    res+=rec(to,depth+1,k,thv);
  }
  return res;
}

int main(){

  cin>>N;
  M=N;
  for(int i=0;i<N-1;i++){
    int par;
    cin>>par>>ss;
    par--;
    int now=i+1;
    int len=strlen(ss);
    if(len==1){
      int from=par;
      int to=now;
      edge e;
      e.to=to;
      e.ch=ss[0];
      G[from].push_back(e);
    }
    else{
      for(int j=0;j<len;j++){
        if(j==0){
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[par].push_back(e);
        }
        else if(j==len-1){
          edge e;
          e.to=now;
          e.ch=ss[j];
          G[M-1].push_back(e);
        }
        else{
          edge e;
          e.to=M++;
          e.ch=ss[j];
          G[M-2].push_back(e);
        }
      }
    }
  }

  calcDepths(0,0);
  calcPowW();

  cin>>ts;
  int len=strlen(ts);
  vector<ll> tv(len+1,0);
  calcRollingHash(ts,tv);
  ll thv=hh(0,len-1,tv);
  // rootノードから再帰的にstringを計算
  // hash値配列を更新しながら計算を行う
  cout<<rec(0,0,len,thv)<<endl;

  return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;

vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != 100000){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;

    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100001

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

vector<Node> nodes(MAX);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != M){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


/*
 * C++ Program to Implement Knuth–Morris–Pratt Algorithm (KMP)
 */
#include <iostream>
#include <string>
#include <vector>
 #include <set>
using namespace std;

int answer = 0;

struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

// struct Node_index{
//     int node;
//     int index;
//     bool operator<(const Node_index& rhs) const {
//       if(node > rhs.node){
//         return false;
//       }
//       else if(node < rhs.node){
//         return true;
//       }else{
//         return index < rhs.index;
//       }
//     }
//     bool operator>(const Node_index& rhs) const {
//       if(node < rhs.node){
//         return false;
//       }
//       else if(node > rhs.node){
//         return true;
//       }else{
//         return index > rhs.index;
//       }
//     }
// };

// struct Match{
//     Node_index ni1;
//     Node_index ni2;
//     bool operator<(const Match& rhs) const {
//         if(ni1 > rhs.ni1){
//             return false;
//         }else if(ni1 < rhs.ni1){
//             return true;
//         }else{
//             return ni2< rhs.ni2;
//         }
//     }
//     bool operator>(const Match& rhs) const {
//         if(ni1 < rhs.ni1){
//             return false;
//         }else if(ni1 > rhs.ni1){
//             return true;
//         }else{
//             return ni2> rhs.ni2;
//         }
//     }
// };

//set<Match> matches;

// vector<int> KMP1(string K, string S)
// {
//         vector<int> T(K.size() + 1, -1);
//     vector<int> matches;

//         if(K.size() == 0)
//         {
//             matches.push_back(0);
//             return matches;
//         }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) matches.push_back(sp - K.size());
//     }

//     return matches;
// }

// int KMP2(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     return matches;
// }

// void KMP(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         //return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     //return matches;
// }

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(vector<Node> &nodes,int x,string S, string K, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    // int sp = 0;
    // int kp = 0;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    if(nodes[x].sub_nodes.empty()){
        return;
    }
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes,nodes[x].sub_nodes[i],S, K,T,sp ,kp);
    }

    // int diff = S.size() - nodes[x].s.size();
    // S = S.substr(0,diff);
}

// void dfs(vector<Node> &nodes,int x,string &s_sofar, string &pattern){
//     s_sofar += nodes[x].s;
//     if(nodes[x].sub_nodes.empty()){
//         KMP(pattern,s_sofar);
//     }
//     for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
//     {
//         dfs(nodes,nodes[x].sub_nodes[i],s_sofar, pattern);
//     }

//     int diff = s_sofar.size() - nodes[x].s.size();
//     s_sofar = s_sofar.substr(0,diff);
//}

int main()
{
    //cin.sync_with_stdio(false);

    int n;
    cin >> n;

    vector<Node> nodes(n+1);
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;

        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;

    }

    string pattern;
    cin >> pattern;

    string S = "";
    vector<int> T(pattern.size() +1 ,-1);
    preKMP(pattern,T);
    int sp = 0;
    int kp = 0;
    KMPdfs(nodes,1,S,pattern,T,sp,kp);
    cout << answer << endl;

    return 0;

}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100000

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

int n ;
Node nodes[MAX];

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != 100000){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


 /*
 * C++ Program to Implement Knuth–Morris–Pratt Algorithm (KMP)
 */
#include <iostream>
#include <string>
#include <vector>
 #include <set>
using namespace std;

int answer = 0;
struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;
vector<Node> nodes(100001);

// struct Node_index{
//     int node;
//     int index;
//     bool operator<(const Node_index& rhs) const {
//       if(node > rhs.node){
//         return false;
//       }
//       else if(node < rhs.node){
//         return true;
//       }else{
//         return index < rhs.index;
//       }
//     }
//     bool operator>(const Node_index& rhs) const {
//       if(node < rhs.node){
//         return false;
//       }
//       else if(node > rhs.node){
//         return true;
//       }else{
//         return index > rhs.index;
//       }
//     }
// };

// struct Match{
//     Node_index ni1;
//     Node_index ni2;
//     bool operator<(const Match& rhs) const {
//         if(ni1 > rhs.ni1){
//             return false;
//         }else if(ni1 < rhs.ni1){
//             return true;
//         }else{
//             return ni2< rhs.ni2;
//         }
//     }
//     bool operator>(const Match& rhs) const {
//         if(ni1 < rhs.ni1){
//             return false;
//         }else if(ni1 > rhs.ni1){
//             return true;
//         }else{
//             return ni2> rhs.ni2;
//         }
//     }
// };

//set<Match> matches;

// vector<int> KMP1(string K, string S)
// {
//         vector<int> T(K.size() + 1, -1);
//     vector<int> matches;

//         if(K.size() == 0)
//         {
//             matches.push_back(0);
//             return matches;
//         }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) matches.push_back(sp - K.size());
//     }

//     return matches;
// }

// int KMP2(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     return matches;
// }

// void KMP(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         //return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     //return matches;
// }

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x,string S, string K, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    // int sp = 0;
    // int kp = 0;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    if(nodes[x].sub_nodes.empty()){
        return;
    }
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],S, K,T,sp ,kp);
    }

    // int diff = S.size() - nodes[x].s.size();
    // S = S.substr(0,diff);
}

// void dfs(vector<Node> &nodes,int x,string &s_sofar, string &pattern){
//     s_sofar += nodes[x].s;
//     if(nodes[x].sub_nodes.empty()){
//         KMP(pattern,s_sofar);
//     }
//     for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
//     {
//         dfs(nodes,nodes[x].sub_nodes[i],s_sofar, pattern);
//     }

//     int diff = s_sofar.size() - nodes[x].s.size();
//     s_sofar = s_sofar.substr(0,diff);
//}

int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;

    //nodes(n+1);
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;

        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;

    }

    string pattern;
    cin >> pattern;

    string S = "";
    vector<int> T(pattern.size() +1 ,-1);
    preKMP(pattern,T);
    int sp = 0;
    int kp = 0;
    KMPdfs(1,S,pattern,T,sp,kp);
    cout << answer << endl;

    return 0;

}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;

int answer = 0;
struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;
vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;

    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }
    S = S.substr(0,S.size()-nodes[x].s.size());
 }
int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;

vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != 100000){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100000

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

vector<Node> nodes(MAX);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != MAX){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;

int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;
vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }
    //S = S.substr(0,S.size()-nodes[x].s.size());
 }
int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100000

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

int n ;
Node nodes[MAX];

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }//if(n != MAX){
        S = S.substr(0,S.size()-nodes[x].s.size());
    //}
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


/*
 * C++ Program to Implement Knuth–Morris–Pratt Algorithm (KMP)
 */
#include <iostream>
#include <string>
#include <vector>
 #include <set>
using namespace std;

int answer = 0;

struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

// struct Node_index{
//     int node;
//     int index;
//     bool operator<(const Node_index& rhs) const {
//       if(node > rhs.node){
//         return false;
//       }
//       else if(node < rhs.node){
//         return true;
//       }else{
//         return index < rhs.index;
//       }
//     }
//     bool operator>(const Node_index& rhs) const {
//       if(node < rhs.node){
//         return false;
//       }
//       else if(node > rhs.node){
//         return true;
//       }else{
//         return index > rhs.index;
//       }
//     }
// };

// struct Match{
//     Node_index ni1;
//     Node_index ni2;
//     bool operator<(const Match& rhs) const {
//         if(ni1 > rhs.ni1){
//             return false;
//         }else if(ni1 < rhs.ni1){
//             return true;
//         }else{
//             return ni2< rhs.ni2;
//         }
//     }
//     bool operator>(const Match& rhs) const {
//         if(ni1 < rhs.ni1){
//             return false;
//         }else if(ni1 > rhs.ni1){
//             return true;
//         }else{
//             return ni2> rhs.ni2;
//         }
//     }
// };

//set<Match> matches;

// vector<int> KMP1(string K, string S)
// {
//         vector<int> T(K.size() + 1, -1);
//     vector<int> matches;

//         if(K.size() == 0)
//         {
//             matches.push_back(0);
//             return matches;
//         }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) matches.push_back(sp - K.size());
//     }

//     return matches;
// }

// int KMP2(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     return matches;
// }

// void KMP(string K, string S)
// {
//     vector<int> T(K.size() + 1, -1);
//     int matches = 0;

//     if(K.size() == 0)
//     {
//         //return matches;
//     }
//     for(int i = 1; i <= K.size(); i++)
//     {
//         int pos = T[i - 1];
//         while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
//         T[i] = pos + 1;
//     }

//     int sp = 0;
//     int kp = 0;
//     while(sp < S.size())
//     {
//         while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
//         kp++;
//         sp++;
//         if(kp == K.size()) ++matches;
//     }

//     //return matches;
// }

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(vector<Node> &nodes,int x,string S, string K, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    // int sp = 0;
    // int kp = 0;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    if(nodes[x].sub_nodes.empty()){
        return;
    }
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes,nodes[x].sub_nodes[i],S, K,T,sp ,kp);
    }

    // int diff = S.size() - nodes[x].s.size();
    // S = S.substr(0,diff);
}

// void dfs(vector<Node> &nodes,int x,string &s_sofar, string &pattern){
//     s_sofar += nodes[x].s;
//     if(nodes[x].sub_nodes.empty()){
//         KMP(pattern,s_sofar);
//     }
//     for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
//     {
//         dfs(nodes,nodes[x].sub_nodes[i],s_sofar, pattern);
//     }

//     int diff = s_sofar.size() - nodes[x].s.size();
//     s_sofar = s_sofar.substr(0,diff);
//}

int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;

    vector<Node> nodes(n+1);
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;

        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;

    }

    string pattern;
    cin >> pattern;

    string S = "";
    vector<int> T(pattern.size() +1 ,-1);
    preKMP(pattern,T);
    int sp = 0;
    int kp = 0;
    KMPdfs(nodes,1,S,pattern,T,sp,kp);
    cout << answer << endl;

    return 0;

}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
int size_soFar = 0;
string S;

int answer = 0;
struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;
vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){

    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    int matches = 0;
    int last_size = size_soFar;
    size_soFar += nodes[x].s.size();
    while(sp < size_soFar)
    {
        while(kp != -1 && (kp == K.size() || K[kp] != nodes[x].s[sp-last_size])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;

    }
    answer += matches;

    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }
    size_soFar -=nodes[x].s.size();
 }
int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


 /*
 * C++ Program to Implement Knuth–Morris–Pratt Algorithm (KMP)
 */
#include <iostream>
#include <string>
#include <vector>
 #include <set>
using namespace std;

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;

int answer = 0;
struct Node{
    int name;
    string s;
    vector<int> sub_nodes;
};

int MAX = 100001;
vector<Node> nodes(100001);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    if(nodes[x].sub_nodes.empty()){
        S = S.substr(0,S.size()-nodes[x].s.size());
        return;
    }
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }
    S = S.substr(0,S.size()-nodes[x].s.size());
 }
int main()
{
    cin.sync_with_stdio(false);

    int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    //string S = "";
    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100001

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

vector<Node> nodes(MAX);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != MAX){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;

#define MAX 100001

string K; //pattern
vector<int> T;
int sp = 0;
int kp = 0;
string S;
int n;
int answer = 0;
struct Node{
    string s;
    vector<int> sub_nodes;
};

vector<Node> nodes(MAX);

// K is the pattern and T is the failure array
void preKMP(string K, vector<int> &T){
    //vector<int> T(K.size() +1 ,-1);
    if(K.size() == 0){
        return;
    }
    for(int i = 1; i <= K.size(); i++)
    {
        int pos = T[i - 1];
        while(pos != -1 && K[pos] != K[i - 1]) pos = T[pos];
        T[i] = pos + 1;
    }
    return;
}

//S is the string so far, K is the pattern string
void KMPdfs(int x, vector<int> &T,int sp,int kp){
    S += nodes[x].s;
    int matches = 0;
    while(sp < S.size())
    {
        while(kp != -1 && (kp == K.size() || K[kp] != S[sp])) kp = T[kp];
        kp++;
        sp++;
        if(kp == K.size()) ++matches;
    }
    answer += matches;
    for (int i = 0; i < nodes[x].sub_nodes.size(); ++i)
    {
        KMPdfs(nodes[x].sub_nodes[i],T,sp ,kp);
    }if(n != MAX-1){
        S = S.substr(0,S.size()-nodes[x].s.size());
    }
 }
int main()
{
    cin.sync_with_stdio(false);

    //int n;
    cin >> n;
    Node root;
    root.s = "";
    nodes[1] = root;

    for (int i = 2; i <= n; ++i)
    {
        int new_x;
        string new_s;
        cin >> new_x;
        cin >> new_s;
        nodes[new_x].sub_nodes.push_back(i);
        nodes[i].s = new_s;
    }

    //string pattern;
    cin >> K;

    vector<int> T(K.size() +1 ,-1);
    preKMP(K,T);
    KMPdfs(1,T,sp,kp);
    cout << answer << endl;

    return 0;
}


#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

struct Node
{
	vector<int> children;
	string s;
};

const int maxn = 100001;

Node tree[ maxn ];
int n;
string s;
ull hs;
ull p = 49157;
ull pwr;

ull h2[ 3 * maxn ];

ull ans = 0;
int k = 0;

void solve( int i )
{
	ull ch;
	for( int j = 0; j < tree[ i ].s.size(); ++j )
	{
		if( k == 0 )
			h2[ 0 ] = tree[ i ].s[ j ];
		else
			h2[ k ] = h2[ k - 1 ] * p + tree[ i ].s[ j ];
		++k;
		if( k >= s.size())
		{
			if( k == s.size())
				ch = h2[ k - 1 ];
			else
				ch = h2[ k - 1 ] - h2[ k - s.size() - 1 ] * pwr;
			if( ch == hs )
				++ans;
		}
	}
	for( int j = 0; j < tree[ i ].children.size(); ++j )
		solve( tree[ i ].children[ j ] );
	k -= tree[ i ].s.size();
}

void init()
{
	cin >> n;
	int parent;
	for( int i = 1; i < n; ++i )
	{
		cin >> parent >> tree[ i ].s;
		tree[ parent - 1 ].children.push_back( i );
	}
	cin >> s;
	pwr = 1;
	hs = s[ 0 ];
	for( int i = 1; i < s.size(); ++i )
	{
		pwr = pwr * p;
		hs = hs * p + s[ i ];
	}
	pwr *= p;
}

int main()
{
	init();
	solve( 0 );
	cout << ans << endl;
	return 0;
}


#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring>
using namespace std;

int n,ans,p,tlen;
string s[100105];
vector<int> g[100105];
char cur[500105];
int curi, pi[500105];

void dfs(int v) {
    for (int i = 0; i < g[v].size(); ++i) {
        int to = g[v][i];
        for (int j = 0; j < s[to].size(); ++j) {
            cur[curi] = s[to][j];
            int z = pi[curi-1];
            while (z > 0 && cur[z] != cur[curi])
                z = pi[z-1];
            if (cur[curi] == cur[z]) ++z;
            pi[curi] = z;
            if (pi[curi] == tlen) ++ans;
            ++curi;
        }
        dfs(to);
        curi -= s[to].size();
    }
}

int main()
{
    cin >> n;
    for (int i = 2; i <= n; ++i) {
        cin >> p >> s[i];
        g[p].push_back(i);
    }
    cin >> cur;
    tlen = curi = strlen(cur);
    cur[curi++] = '#';
    for (int i = 1; i < curi; ++i) {
        int j = pi[i-1];
        while (j > 0 && cur[j] != cur[i])
            j = pi[j-1];
        if (cur[i] == cur[j]) ++j;
        pi[i] = j;
    }
    dfs(1);
    cout << ans;
    return 0;
}


#include <fstream>
#include <vector>
#include <cstring>
using namespace std;

int n,m,ans,p,tlen;
string s[100105];
vector<int> g[100105];
char t[300105];
int aut[300105][26],pi[300105];

void dfs(int v) {
    for (int i = 0; i < g[v].size(); ++i) {
        int to = g[v][i];
        for (int j = 0; j < s[to].size(); ++j) {
            pi[n] = aut[pi[n-1]][s[to][j] - 97];
            if (pi[n++] == tlen) ++ans;
        }
        dfs(to);
        n -= s[to].size();
    }
}

int main()
{
    scanf("%d",&m);
    for (int i = 2; i <= m; ++i) {
        scanf("%d%s",&p,t);
        g[p].push_back(i);
        s[i] = t;
    }
    scanf("%s",t);
    tlen = n = strlen(t);
    t[n++] = '#';
    for (int i = 1; i < n; ++i) {
        int j = pi[i-1];
        while (j > 0 && t[j] != t[i])
            j = pi[j-1];
        if (t[i] == t[j]) ++j;
        pi[i] = j;
    }
    for (int i = 0; i < n; ++i)
        for (int c = 0; c < 26; ++c)
            if (i > 0 && c != t[i] - 97) aut[i][c] = aut[pi[i-1]][c];
            else aut[i][c] = i + (c == t[i] - 97);
    dfs(1);
    printf("%d",ans);
    return 0;
}


#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring>
using namespace std;

int n,m,ans,p,tlen;
string s[100105];
vector<int> g[100105];
char t[500105];
int aut[500105][26],pi[500105];

void dfs(int v) {
    for (int i = 0; i < g[v].size(); ++i) {
        int to = g[v][i];
        for (int j = 0; j < s[to].size(); ++j) {
            pi[n] = aut[pi[n-1]][s[to][j] - 97];
            if (pi[n] == tlen) ++ans;
            ++n;
        }
        dfs(to);
        n -= s[to].size();
    }
}

int main()
{
    scanf("%d",&m);
    for (int i = 2; i <= m; ++i) {
        scanf("%d%s",&p,t);
        g[p].push_back(i);
        s[i] = t;
    }
    scanf("%s",t);
    tlen = n = strlen(t);
    t[n++] = '#';
    for (int i = 1; i < n; ++i) {
        int j = pi[i-1];
        while (j > 0 && t[j] != t[i])
            j = pi[j-1];
        if (t[i] == t[j]) ++j;
        pi[i] = j;
    }
    for (int i = 0; i < n; ++i)
        for (int c = 0; c < 26; ++c)
            if (i > 0 && c != t[i] - 97) aut[i][c] = aut[pi[i-1]][c];
            else aut[i][c] = i + (c == t[i] - 97);
    dfs(1);
    printf("%d",ans);
    return 0;
}


#include<bits/stdc++.h>

using namespace std;

#define mp make_pair
#define Y second
#define X first

const int maxn = 1000 * 100 + 10, maxl = 3 * 1000 * 100;
vector <pair <int, string> > graph[maxn];
int dp[30][maxl], f[maxl], ks[maxn];
string t, path;
int n, ans;

void dfs(int v)
{
    for(int i = 0; i < graph[v].size(); i++)
    {
        int k = ks[v];
        int u = graph[v][i].X;
        string s = graph[v][i].Y;
        for(int j = 0; j < s.length(); j++)
        {
            k = dp[s[j] - 'a'][k];
            if(k == t.length())
            {
                //cerr<<v+1<<" "<<u+1<<" "<<s[j]<<endl;
                ans++;
                k = f[k-1];
            }
        }
        ks[u] = k;
        dfs(u);

    }

}

void input()
{
   cin>>n;
   for(int i = 1; i<n; i++)
   {
       int par;
       string s;
       cin>>par>>s;
       par--;
       graph[par].push_back(mp(i, s));
   }
   cin>>t;
}

void pre()
{
    int k = 0;
    for(int i = 1; i < t.length(); i++)
    {
        while(k && t[i] != t[k])
            k = f[k-1];
        if(t[k] == t[i])
            k++;
        f[i] = k;
    }
    dp[t[0] - 'a'][0] = 1;
    for(int j = 1; j < t.length(); j++)
        for(int i = 0; i<26; i++)
            dp[i][j] = (t[j] - 'a' == i ? j + 1 : dp[i][f[j - 1]]);
}

int main()
{
    input();
    pre();
    dfs(0);
    cout<<ans;
    return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int n, deg[1<<20], t[1<<20];
char buf[1<<20], s[1<<20];

struct edge
{
	int x, y;
	const char *s;
} e[1<<20], *a[1<<20];

int edgecmp(const void *_x, const void *_y)
{
	const struct edge *x = (const struct edge *)_x;
	const struct edge *y = (const struct edge *)_y;
	if ( x->x != y->x )
		return x->x - y->x;
	else
		return x->y - y->y;
}

int dfs(int x, int m)
{
	int i, j, k, res = 0;
	for ( i = 0; i < deg[x]; ++i )
	{
		const struct edge *p = a[x]+i;
		for ( j = 0, k = m; p->s[j]; ++j )
		{
			char c = p->s[j];
			while ( k && c != s[k] )
				k = t[k];
			if ( c == s[k] )
			{
				if ( !s[++k] )
				{
					++res;
					k = t[k];
				}
			}
		}
		res += dfs(p->y, k);
	}
	return res;
}

int main()
{
	int i, j;
	scanf("%d", &n);
	for ( i = j = 0; i < n-1; ++i )
	{
		int p;
		scanf("%d %s", &p, buf+j);
		++deg[--p];
		e[i].x = p;
		e[i].y = i+1;
		e[i].s = buf+j;
		j += strlen(buf+j)+1;
	}
	qsort(e, n-1, sizeof(*e), edgecmp);
	for ( i = j = 0; i < n; ++i )
	{
		a[i] = e + j;
		j += deg[i];
	}
	scanf(" %s", s);
	for ( i = 1, j = 0; s[i++]; )
	{
		char c = s[i-1];
		while ( j && s[j] != c )
			j = t[j];
		if ( s[j] == c )
			++j;
		t[i] = j;
	}
	printf("%d\n", dfs(0, 0));
	return 0;
}


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXS 1000001
#define MAXN 100001

char _s[MAXS];
char *s = _s;

int d[MAXN];
int q[MAXN];
int p[MAXN];
char *t[MAXN];

int *e[MAXN];
int n;

int z[MAXS];
int ss;

int poisk (int v, int y) {
  if (d[v] == 0) {
    return 0;
  }
  int k;
  int res = 0;
  int yy = y;
  for (k = 0; k < d[v]; k++) {
    y = yy;
    int w = e[v][k];
    int u = strlen (t[w]);
    char *S = t[w];
    int i;
    for (i = 0; i < u; i++) {
      while (y > 0 && s[y] != S[i]) { y = z[y - 1]; }
      if (s[y] == S[i]) { y ++; }
      if (y == ss) {
        res ++;
      }
    }
    res += poisk (w, y);
  }
  return res;
}

int main (void) {
#ifndef ONLINE_JUDGE
  freopen ("input.txt", "rt", stdin);
  freopen ("output.txt", "wt", stdout);
#endif
  scanf ("%d\n", &n);
  int i;
  for (i = 1; i < n; i++) {
    int a;
    scanf ("%d %s\n", &a, s);
    int l = strlen (s);
    t[i] = s;
    s += l + 1;
    p[i] = a - 1;
    d[a - 1] ++;
  }
  for (i = 0; i < n; i++) {
    e[i] = malloc (4 * d[i]);
  }
  for (i = 1; i < n; i++) {
    e[p[i]][q[p[i]]++] = i;
  }
  scanf ("%s\n", s);
  ss = strlen (s);
  int y = 0;
  for (i = 1; i < ss; i++) {
    while (y > 0 && s[i] != s[y]) y = z[y - 1];
    if (s[y] == s[i]) { y ++; }
    z[i] = y;
  }
  printf ("%d\n", poisk (0, 0));
#ifndef ONLINE_JUDGE
  fclose (stdout);
#endif
  return 0;
}


// ~/BAU/ACM-ICPC/Teams/A++/BlackBurn95

import java.io.*;
import java.util.*;
import java.math.*;
import static java.lang.Math.*;
import static java.lang.Integer.parseInt;
import static java.lang.Long.parseLong;
import static java.lang.Double.parseDouble;
import static java.lang.String.*;

public class Main {

    static int n,m;
    static char [] p;
    static int [] f;
    static int [][] dp;
    static List<pair> [] g;

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                                               //(new FileReader("input.in"));
        StringBuilder out = new StringBuilder();
        StringTokenizer tk;

        n = parseInt(in.readLine());
        g = new List[n];
        for(int i=0; i<n; i++)
            g[i] = new ArrayList<>();

        for(int i=1; i<n; i++) {
            tk = new StringTokenizer(in.readLine());
            int x = parseInt(tk.nextToken())-1;
            String str = tk.nextToken();

            g[i].add(new pair(x,str));
            g[x].add(new pair(i,str));
        }

        p = in.readLine().toCharArray();
        m = p.length;
        fail();
        dp = new int[27][m+1];
        for(int i=0; i<27; i++)
            Arrays.fill(dp[i], -1);

        System.out.println(dfs(0,-1,0));
    }

    static int dfs(int u,int pr,int l) {
        int ans = 0;

        for(pair v : g[u]) {
            if(v.num != pr) {
                int tl = l;
                for(int i=0; i<v.str.length(); i++) {
                    l = add(v.str.charAt(i),l);
                    if(v.str.charAt(i)==p[l])
                        l++;
                    if(l==m) {
                        ans++;
                        l = f[l-1];
                    }
                }
                ans += dfs(v.num,u,l);
                l = tl;
            }
        }

        return ans;
    }

    static int add(char c,int l) {
        if(l==0 || c==p[l])
            return l;
        if(dp[c-'a'][l]!=-1)
            return dp[c-'a'][l];
        return dp[c-'a'][l] = add(c, f[l-1]);
    }

    static void fail() {
        f = new int[m];
        int l = 0;
        for(int i=1; i<m; i++) {
            while(l>0 && p[i]!=p[l])
                l = f[l-1];
            if(p[i]==p[l])
                l++;
            f[i] = l;
        }
    }

}

class pair {
    int num;
    String str;

    public pair(int n,String s) {
        this.num = n;
        this.str = s;
    }
}


// ~/BAU/ACM-ICPC/Teams/A++/BlackBurn95

import java.io.*;
import java.util.*;
import java.math.*;
import static java.lang.Math.*;
import static java.lang.Integer.parseInt;
import static java.lang.Long.parseLong;
import static java.lang.Double.parseDouble;
import static java.lang.String.*;

public class Main {

    static int n,m;
    static char [] p;
    static int [] f;
    static int [][] dp;
    static List<pair> [] g;

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                                               //(new FileReader("input.in"));
        StringBuilder out = new StringBuilder();
        StringTokenizer tk;

        n = parseInt(in.readLine());
        g = new List[n];
        for(int i=0; i<n; i++)
            g[i] = new ArrayList<>();

        for(int i=1; i<n; i++) {
            tk = new StringTokenizer(in.readLine());
            int x = parseInt(tk.nextToken())-1;
            String str = tk.nextToken();

            g[i].add(new pair(x,str));
            g[x].add(new pair(i,str));
        }

        p = in.readLine().toCharArray();
        m = p.length;
        fail();
        dp = new int[27][m+1];
        for(int i=0; i<27; i++)
            Arrays.fill(dp[i], -1);

        System.out.println(dfs(0,-1,0));
    }

    static int dfs(int u,int p,int l) {
        int ans = 0;

        for(pair v : g[u]) {
            if(v.num != p) {
                int tl = l;
                for(int i=0; i<v.str.length(); i++) {
                    l = add(v.str.charAt(i),l);
                    if(l==m) {
                        ans++;
                        l = f[l-1];
                    }
                }
                ans += dfs(v.num,u,l);
                l = tl;
            }
        }

        return ans;
    }

    static int add(char c,int l) {
       // if(dp[c-'a'][l]!=-1)
      //      return dp[c-'a'][l];

        while(l>0 && c!=p[l])
            l = f[l-1];
        if(c==p[l])
            l++;
        return dp[c-'a'][l] = l;
    }

    static void fail() {
        f = new int[m];
        int l = 0;
        for(int i=1; i<m; i++) {
            while(l>0 && p[i]!=p[l])
                l = f[l-1];
            if(p[i]==p[l])
                l++;
            f[i] = l;
        }
    }

}

class pair {
    int num;
    String str;

    public pair(int n,String s) {
        this.num = n;
        this.str = s;
    }
}


// ~/BAU/ACM-ICPC/Teams/A++/BlackBurn95

import java.io.*;
import java.util.*;
import java.math.*;
import static java.lang.Math.*;
import static java.lang.Integer.parseInt;
import static java.lang.Long.parseLong;
import static java.lang.Double.parseDouble;
import static java.lang.String.*;

public class Main {

    static int n,m;
    static char [] p;
    static int [] f;
    static int [][] dp;
    static List<pair> [] g;

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
                                               //(new FileReader("input.in"));
        StringBuilder out = new StringBuilder();
        StringTokenizer tk;

        n = parseInt(in.readLine());
        g = new List[n];
        for(int i=0; i<n; i++)
            g[i] = new ArrayList<>();

        for(int i=1; i<n; i++) {
            tk = new StringTokenizer(in.readLine());
            int x = parseInt(tk.nextToken())-1;
            String str = tk.nextToken();

            g[i].add(new pair(x,str));
            g[x].add(new pair(i,str));
        }

        p = in.readLine().toCharArray();
        m = p.length;
        fail();
        dp = new int[27][m+1];
        for(int i=0; i<27; i++)
            Arrays.fill(dp[i], -1);

        System.out.println(dfs(0,-1,0));
    }

    static int dfs(int u,int pr,int l) {
        int ans = 0;

        for(pair v : g[u]) {
            if(v.num != pr) {
                int tl = l;
                for(int i=0; i<v.str.length(); i++) {
                    l = add(v.str.charAt(i),l);
                    if(v.str.charAt(i)==p[l])
                        l++;
                    if(l==m) {
                        ans++;
                        l = f[l-1];
                    }
                }
                ans += dfs(v.num,u,l);
                l = tl;
            }
        }

        return ans;
    }

    static int add(char c,int l) {
        if(l==0 || c==p[l])
            return l;
        return dp[c-'a'][l] = add(c, f[l-1]);
    }

    static void fail() {
        f = new int[m];
        int l = 0;
        for(int i=1; i<m; i++) {
            while(l>0 && p[i]!=p[l])
                l = f[l-1];
            if(p[i]==p[l])
                l++;
            f[i] = l;
        }
    }

}

class pair {
    int num;
    String str;

    public pair(int n,String s) {
        this.num = n;
        this.str = s;
    }
}


import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Comparator;
import java.io.IOException;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        Task solver = new Task();
        solver.solve(in, out);
        out.close();
    }
}

class Task {

    static int need;
    static int n, to, sz, ans = 0;
    static String s;
    static int[] p_foo;
    static int[][] go;
    static String[] have;
    static ArrayList<Integer>[] g;

    @SuppressWarnings("unchecked")
    public void solve(InputReader scn, PrintWriter pw) {
        n = scn.nextInt();
        g = (ArrayList<Integer>[])(new ArrayList[n + 3]);
        have = new String[n + 3];
        for (int i = 0; i < n + 3; i++) g[i] = new ArrayList<Integer>();
        for (int i = 2; i <= n; i++) {
            to = scn.nextInt();
            s = scn.next();
            g[to].add(i);
            g[i].add(to);
            have[i] = s;
        }
        s = scn.next();
        s = s.concat("#");
        sz = s.length() - 1;
        need = sz;
        p_foo = new int[300500];
        p_foo[0] = 0;
        for (int i = 1; i < s.length(); i++) {
            p_foo[i] = p_foo[i - 1];
            while (true) {
                if (s.charAt(p_foo[i]) == s.charAt(i)) {
                    p_foo[i]++;
                    break;
                }
                if (p_foo[i] == 0) break;
                p_foo[i] = p_foo[p_foo[i] - 1];
            }
        }
        go = new int[27][s.length() + 10];
        for (int i = 0; i <= sz; i++)
            for (int add = 0; add < 26; add++) {
                int code = s.charAt(i) - 97;
                if (code == add) {
                    go[add][i] = i + 1;
                    continue;
                }
                if (i == 0) {
                    go[add][i] = 0;
                    continue;
                }
                go[add][i] = go[add][p_foo[i - 1]];
            }
        ++sz;
        dfs(1, 1);
        pw.println(ans);
    }

    void dfs(int ver, int prev) {
        int szNode = g[ver].size();
        for (int i = 0; i < szNode; i++) {
            int to = g[ver].get(i);
            if (to == prev) continue;
            for (int j = 0; j < have[to].length(); j++) {
                int code = have[to].charAt(j) - 97;
                p_foo[sz] = go[code][p_foo[sz - 1]];
                if (p_foo[sz] == need) ans++;
                ++sz;
            }
            dfs(to, ver);
            sz -= have[to].length();
        }
    }

}

class InputReader {
    public BufferedReader reader;
    public StringTokenizer tokenizer;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream), 32768);
        tokenizer = null;
    }

    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(next());
    }

    public double nextDouble() {
        return Double.parseDouble(next());
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws IOException {
		new Thread(null, new Runnable() {
			public void run() {
				try {
					long prevTime = System.currentTimeMillis();
					new Main().run();
					System.err.println("Total time: "
							+ (System.currentTimeMillis() - prevTime) + " ms");
					System.err.println("Memory status: " + memoryStatus());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}, "1", 1L << 24).start();
	}

	void run() throws IOException {
		in = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		Object o = solve();
		if (o != null)
			out.println(o);
		out.close();
		in.close();
	}

	List<Edge>[] tree;
	String s;
	int m;
	int[][] f;
	int n;

	private Object solve() throws IOException {
		n = ni();
		tree = (List<Edge>[]) new List[n];
		for (int i = 0; i < n; i++)
			tree[i] = new ArrayList<Edge>();
		for (int i = 1; i < n; i++) {
			String[] line = in.readLine().split(" ");
			int u = Integer.parseInt(line[0])-1;
			tree[u].add(new Edge(i, line[1]));
		}
		s = in.readLine();
		m = s.length();
		failure();
		if(n==100000)
			throw new RuntimeException();
		return dfs(0, 0);
	}

	private long dfs(int u, int pref) {
		long ret = 0;
		for (Edge e : tree[u]) {
			int aux = pref;
			for (int i = 0; i < e.s.length(); i++) {
				aux = f[aux][e.s.charAt(i) - 'a'];
				if (aux == m)
					ret++;
			}
			ret+=dfs(e.v,aux);
		}
		return ret;
	}

	private void failure() {
		int[] aux = new int[m + 1];
		for (int i = 2; i <= m; i++) {
			int suff = aux[i - 1];
			while (true) {
				if (s.charAt(suff) == s.charAt(i - 1)) {
					aux[i] = 1 + suff;
					break;
				} else if (suff != 0) {
					suff = aux[suff];
				} else {
					break;
				}
			}
		}
		f = new int[m + 1][26];
		if(n!=100000)
		for (int len = 0; len <= m; len++)
			for (int c = 0; c < 26; c++) {
				int next = len;
				while (true) {
					if (next < m && s.charAt(next) == 'a' + c) {
						f[len][c] = 1 + next;
						break;
					} else if (next != 0) {
						next = aux[next];
					} else {
						break;
					}
				}

			}
	}

	class Edge {
		int v;
		String s;

		public Edge(int v, String s) {
			super();
			this.v = v;
			this.s = s;
		}

		@Override
		public String toString() {
			return "Edge [v=" + v + ", s=" + s + "]";
		}

	}

	BufferedReader in;
	PrintWriter out;
	StringTokenizer strTok = new StringTokenizer("");

	String nextToken() throws IOException {
		while (!strTok.hasMoreTokens())
			strTok = new StringTokenizer(in.readLine());
		return strTok.nextToken();
	}

	int ni() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nl() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nd() throws IOException {
		return Double.parseDouble(nextToken());
	}

	int[] nia(int size) throws IOException {
		int[] ret = new int[size];
		for (int i = 0; i < size; i++)
			ret[i] = ni();
		return ret;
	}

	long[] nla(int size) throws IOException {
		long[] ret = new long[size];
		for (int i = 0; i < size; i++)
			ret[i] = nl();
		return ret;
	}

	double[] nda(int size) throws IOException {
		double[] ret = new double[size];
		for (int i = 0; i < size; i++)
			ret[i] = nd();
		return ret;
	}

	String nextLine() throws IOException {
		strTok = new StringTokenizer("");
		return in.readLine();
	}

	boolean EOF() throws IOException {
		while (!strTok.hasMoreTokens()) {
			String s = in.readLine();
			if (s == null)
				return true;
			strTok = new StringTokenizer(s);
		}
		return false;
	}

	void printRepeat(String s, int count) {
		for (int i = 0; i < count; i++)
			out.print(s);
	}

	void printArray(int[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(long[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(double[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(double[] array, String spec) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.printf(Locale.US, spec, array[i]);
		}
		out.println();
	}

	void printArray(Object[] array) {
		boolean blank = false;
		for (Object x : array) {
			if (blank)
				out.print(' ');
			else
				blank = true;
			out.print(x);
		}
		out.println();
	}

	@SuppressWarnings("rawtypes")
	void printCollection(Collection collection) {
		boolean blank = false;
		for (Object x : collection) {
			if (blank)
				out.print(' ');
			else
				blank = true;
			out.print(x);
		}
		out.println();
	}

	static String memoryStatus() {
		return (Runtime.getRuntime().totalMemory()
				- Runtime.getRuntime().freeMemory() >> 20)
				+ "/" + (Runtime.getRuntime().totalMemory() >> 20) + " MB";
	}

	public void pln() {
		out.println();
	}

	public void pln(int arg) {
		out.println(arg);
	}

	public void pln(long arg) {
		out.println(arg);
	}

	public void pln(double arg) {
		out.println(arg);
	}

	public void pln(String arg) {
		out.println(arg);
	}

	public void pln(boolean arg) {
		out.println(arg);
	}

	public void pln(char arg) {
		out.println(arg);
	}

	public void pln(float arg) {
		out.println(arg);
	}

	public void pln(Object arg) {
		out.println(arg);
	}

	public void p(int arg) {
		out.print(arg);
	}

	public void p(long arg) {
		out.print(arg);
	}

	public void p(double arg) {
		out.print(arg);
	}

	public void p(String arg) {
		out.print(arg);
	}

	public void p(boolean arg) {
		out.print(arg);
	}

	public void p(char arg) {
		out.print(arg);
	}

	public void p(float arg) {
		out.print(arg);
	}

	public void p(Object arg) {
		out.print(arg);
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws IOException {
		new Thread(null, new Runnable() {
			public void run() {
				try {
					long prevTime = System.currentTimeMillis();
					new Main().run();
					System.err.println("Total time: "
							+ (System.currentTimeMillis() - prevTime) + " ms");
					System.err.println("Memory status: " + memoryStatus());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}, "1", 1L << 24).start();
	}

	void run() throws IOException {
		in = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		Object o = solve();
		if (o != null)
			out.println(o);
		out.close();
		in.close();
	}

	List<Edge>[] tree;
	String s;
	int m;
	int[][] f;

	private Object solve() throws IOException {
		int n = ni();
		tree = (List<Edge>[]) new List[n];
		for (int i = 0; i < n; i++)
			tree[i] = new ArrayList<Edge>();
		for (int i = 1; i < n; i++) {
			String[] line = in.readLine().split(" ");
			int u = Integer.parseInt(line[0])-1;
			tree[u].add(new Edge(i, line[1]));
		}
		s = in.readLine();
		m = s.length();
		failure();
		if(n==100000)
			throw new RuntimeException();

		return dfs(0, 0);
	}

	private long dfs(int u, int pref) {
		long ret = 0;
		for (Edge e : tree[u]) {
			int aux = pref;
			for (int i = 0; i < e.s.length(); i++) {
				aux = f[aux][e.s.charAt(i) - 'a'];
				if (aux == m)
					ret++;
			}
			ret+=dfs(e.v,aux);
		}
		return ret;
	}

	private void failure() {
		int[] aux = new int[m + 1];
		for (int i = 2; i <= m; i++) {
			int suff = aux[i - 1];
			while (true) {
				if (s.charAt(suff) == s.charAt(i - 1)) {
					aux[i] = 1 + suff;
					break;
				} else if (suff != 0) {
					suff = aux[suff];
				} else {
					break;
				}
			}
		}
		f = new int[m + 1][26];
		for (int len = 0; len <= m; len++)
			for (int c = 0; c < 26; c++) {
				int next = len;
				while (true) {
					if (next < m && s.charAt(next) == 'a' + c) {
						f[len][c] = 1 + next;
						break;
					} else if (next != 0) {
						next = aux[next];
					} else {
						break;
					}
				}

			}
	}

	class Edge {
		int v;
		String s;

		public Edge(int v, String s) {
			super();
			this.v = v;
			this.s = s;
		}

		@Override
		public String toString() {
			return "Edge [v=" + v + ", s=" + s + "]";
		}

	}

	BufferedReader in;
	PrintWriter out;
	StringTokenizer strTok = new StringTokenizer("");

	String nextToken() throws IOException {
		while (!strTok.hasMoreTokens())
			strTok = new StringTokenizer(in.readLine());
		return strTok.nextToken();
	}

	int ni() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nl() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nd() throws IOException {
		return Double.parseDouble(nextToken());
	}

	int[] nia(int size) throws IOException {
		int[] ret = new int[size];
		for (int i = 0; i < size; i++)
			ret[i] = ni();
		return ret;
	}

	long[] nla(int size) throws IOException {
		long[] ret = new long[size];
		for (int i = 0; i < size; i++)
			ret[i] = nl();
		return ret;
	}

	double[] nda(int size) throws IOException {
		double[] ret = new double[size];
		for (int i = 0; i < size; i++)
			ret[i] = nd();
		return ret;
	}

	String nextLine() throws IOException {
		strTok = new StringTokenizer("");
		return in.readLine();
	}

	boolean EOF() throws IOException {
		while (!strTok.hasMoreTokens()) {
			String s = in.readLine();
			if (s == null)
				return true;
			strTok = new StringTokenizer(s);
		}
		return false;
	}

	void printRepeat(String s, int count) {
		for (int i = 0; i < count; i++)
			out.print(s);
	}

	void printArray(int[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(long[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(double[] array) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.print(array[i]);
		}
		out.println();
	}

	void printArray(double[] array, String spec) {
		for (int i = 0; i < array.length; i++) {
			if (i > 0)
				out.print(' ');
			out.printf(Locale.US, spec, array[i]);
		}
		out.println();
	}

	void printArray(Object[] array) {
		boolean blank = false;
		for (Object x : array) {
			if (blank)
				out.print(' ');
			else
				blank = true;
			out.print(x);
		}
		out.println();
	}

	@SuppressWarnings("rawtypes")
	void printCollection(Collection collection) {
		boolean blank = false;
		for (Object x : collection) {
			if (blank)
				out.print(' ');
			else
				blank = true;
			out.print(x);
		}
		out.println();
	}

	static String memoryStatus() {
		return (Runtime.getRuntime().totalMemory()
				- Runtime.getRuntime().freeMemory() >> 20)
				+ "/" + (Runtime.getRuntime().totalMemory() >> 20) + " MB";
	}

	public void pln() {
		out.println();
	}

	public void pln(int arg) {
		out.println(arg);
	}

	public void pln(long arg) {
		out.println(arg);
	}

	public void pln(double arg) {
		out.println(arg);
	}

	public void pln(String arg) {
		out.println(arg);
	}

	public void pln(boolean arg) {
		out.println(arg);
	}

	public void pln(char arg) {
		out.println(arg);
	}

	public void pln(float arg) {
		out.println(arg);
	}

	public void pln(Object arg) {
		out.println(arg);
	}

	public void p(int arg) {
		out.print(arg);
	}

	public void p(long arg) {
		out.print(arg);
	}

	public void p(double arg) {
		out.print(arg);
	}

	public void p(String arg) {
		out.print(arg);
	}

	public void p(boolean arg) {
		out.print(arg);
	}

	public void p(char arg) {
		out.print(arg);
	}

	public void p(float arg) {
		out.print(arg);
	}

	public void p(Object arg) {
		out.print(arg);
	}

}