#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <string>

#include <vector>
#include <map>
#include <set>

#include <algorithm>
#include <cmath>

#include <ctime>
#include <cstdlib>

using namespace std;

#define PATH "C:\\Users\\Malkav\\Desktop\\"
#define debug(x) cerr << fixed << setprecision(3) << "DEBUG: " << #x << " = " << x << endl
#define forn (i, n) for (int i = 0; i < n; ++i)
#define sz(x) ((int)(x.size()))
#define mp make_pair
#define pb push_back

typedef long long ll;
typedef pair<ll, ll> H;

H operator+(H a, const H& b) {
  a.first += b.first;
  a.second += b.second;
  return a;
}

H operator-(H a, const H& b) {
  a.first -= b.first;
  a.second -= b.second;
  return a;
}

H operator*(H a, const H& b) {
  a.first *= b.first;
  a.second *= b.second;
  return a;
}

H operator%(H a, const H& b) {
  a.first %= b.first;
  if (a.first < 0) a.first += b.first;
  a.second %= b.second;
  if (a.second < 0) a.second += b.second;
  return a;
}

H to_h(char c) {
  return mp((ll)c, (ll)c);
}

const int N = 100000;
const int M = 400000;

H p = mp(29ll, 31ll);
H m = mp(1000000007ll, 1000000007ll + 110292ll);
H z = mp(0ll, 0ll);

vector<pair<int, string> > g[N];
vector<H> st(1, z);

H goal_h;
int goal_sz;

void reduce(string& s) {
  for (int i = 0; i < sz(s); ++i) {
    s[i] = s[i] - 'a' + 1;
  }
}

int ans = 0;

H ppow[M + 1];

H calc_h(const string& s) {
  H result = z;
  for (int i = 0; i < sz(s); ++i) {
    result = (result * p + to_h(s[i])) % m;
  }
  return result;
}

void dfs(int v, int old_sz) {
  int cur_sz = sz(st);

  for (int i = max(old_sz, goal_sz); i < cur_sz; ++i) {
    H cur = (st[i] - st[i - goal_sz] * ppow[goal_sz]) % m;
    if (cur == goal_h) {
      ++ans;
    }
  }

  for (int i = 0; i < sz(g[v]); ++i) {
    string& s = g[v][i].second;
    for (int j = 0; j < sz(s); ++j) {
      H new_back = (st.back() * p + to_h(s[j])) % m;
      st.pb(new_back);
    }
    dfs(g[v][i].first, cur_sz);
  }

  while (sz(st) != old_sz) {
    st.pop_back();
  }
}

int main() {
  //freopen(PATH"in.txt", "r", stdin);
  //ios_base::sync_with_stdio(false);

  ppow[0] = mp(1ll, 1ll);
  for (int i = 0; i < M; ++i) {
    ppow[i + 1] = (ppow[i] * p) % m;
  }

  int n;
  cin >> n;
  string s;
  for (int i = 1, p; i < n; ++i) {
    cin >> p >> s;
    reduce(s);
    g[p - 1].pb(mp(i, s));
  }
  cin >> s;
  reduce(s);
  goal_h = calc_h(s);

  goal_sz = sz(s);
  dfs(0, 1);
  cout << ans << endl;

  return 0;
}


#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <string>

#include <vector>
#include <map>
#include <set>

#include <algorithm>
#include <cmath>

#include <ctime>
#include <cstdlib>

using namespace std;

#define PATH "C:\\Users\\Malkav\\Desktop\\"
#define debug(x) cerr << fixed << setprecision(3) << "DEBUG: " << #x << " = " << x << endl
#define forn (i, n) for (int i = 0; i < n; ++i)
#define sz(x) ((int)(x.size()))
#define mp make_pair
#define pb push_back

typedef long long ll;
typedef pair<ll, ll> H;

H operator+(H a, const H& b) {
  a.first += b.first;
  a.second += b.second;
  return a;
}

H operator-(H a, const H& b) {
  a.first -= b.first;
  a.second -= b.second;
  return a;
}

H operator*(H a, const H& b) {
  a.first *= b.first;
  a.second *= b.second;
  return a;
}

H operator%(H a, const H& b) {
  a.first %= a.second;
  if (a.first < 0) a.first += b.first;
  a.second %= b.second;
  if (a.second < 0) a.second += b.second;
  return a;
}

H to_h(char c) {
  return mp((ll)c, (ll)c);
}

const int N = 100000;
const int M = 400000;

H p = mp(29ll, 31ll);
H m = mp(1000000007ll, 1000000007ll + 110292ll);
H z = mp(0ll, 0ll);

vector<pair<int, string> > g[N];
vector<H> st(1, z);

H goal_h;
int goal_sz;

void reduce(string& s) {
  for (int i = 0; i < sz(s); ++i) {
    s[i] = s[i] - 'a' + 1;
  }
}

int ans = 0;

H ppow[M + 1];

H calc_h(const string& s) {
  H result = z;
  for (int i = 0; i < sz(s); ++i) {
    result = (result * p + to_h(s[i])) % m;
  }
  return result;
}

void dfs(int v, int old_sz) {
  int cur_sz = sz(st);

  for (int i = max(old_sz, goal_sz); i < cur_sz; ++i) {
    H cur = (st[i] - st[i - goal_sz] * ppow[goal_sz]) % m;
    if (cur == goal_h) {
      ++ans;
    }
  }

  for (int i = 0; i < sz(g[v]); ++i) {
    string& s = g[v][i].second;
    for (int j = 0; j < sz(s); ++j) {
      H new_back = (st.back() * p + to_h(s[j])) % m;
      st.pb(new_back);
    }
    dfs(g[v][i].first, cur_sz);
  }

  while (sz(st) != old_sz) {
    st.pop_back();
  }
}

int main() {
  //freopen(PATH"in.txt", "r", stdin);
  //ios_base::sync_with_stdio(false);

  ppow[0] = mp(1ll, 1ll);
  for (int i = 0; i < M; ++i) {
    ppow[i + 1] = (ppow[i] * p) % m;
  }

  int n;
  cin >> n;
  string s;
  for (int i = 1, p; i < n; ++i) {
    cin >> p >> s;
    reduce(s);
    g[p - 1].pb(mp(i, s));
  }
  cin >> s;
  reduce(s);
  goal_h = calc_h(s);

  goal_sz = sz(s);
  dfs(0, 1);
  cout << ans << endl;

  return 0;
}


#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <string>

#include <vector>
#include <map>
#include <set>

#include <algorithm>
#include <cmath>

#include <ctime>
#include <cstdlib>

using namespace std;

#define PATH "C:\\Users\\Malkav\\Desktop\\"
#define debug(x) cerr << fixed << setprecision(3) << "DEBUG: " << #x << " = " << x << endl
#define forn (i, n) for (int i = 0; i < n; ++i)
#define sz(x) ((int)(x.size()))
#define mp make_pair
#define pb push_back

typedef long long ll;
typedef pair<ll, ll> H;

H operator+(H a, const H& b) {
  a.first += b.first;
  a.second += b.second;
  return a;
}

H operator-(H a, const H& b) {
  a.first -= b.first;
  a.second -= b.second;
  return a;
}

H operator*(H a, const H& b) {
  a.first *= b.first;
  a.second *= b.second;
  return a;
}

H operator%(H a, const H& b) {
  if (a.first < 0) a.first += b.first;
  a.second %= b.second;
  if (a.second < 0) a.second += b.second;
  return a;
}

H to_h(char c) {
  return mp((ll)c, (ll)c);
}

const int N = 100000;
const int M = 400000;

H p = mp(29ll, 31ll);
H m = mp(1000000007ll, 1000000007ll + 110292ll);
H z = mp(0ll, 0ll);

vector<pair<int, string> > g[N];
vector<H> st(1, z);

H goal_h;
int goal_sz;

void reduce(string& s) {
  for (int i = 0; i < sz(s); ++i) {
    s[i] = s[i] - 'a' + 1;
  }
}

int ans = 0;

H ppow[M + 1];

H calc_h(const string& s) {
  H result = z;
  for (int i = 0; i < sz(s); ++i) {
    result = (result * p + to_h(s[i])) % m;
  }
  return result;
}

void dfs(int v, int old_sz) {
  int cur_sz = sz(st);

  for (int i = max(old_sz, goal_sz); i < cur_sz; ++i) {
    H cur = (st[i] - st[i - goal_sz] * ppow[goal_sz]) % m;
    if (cur == goal_h) {
      ++ans;
    }
  }

  for (int i = 0; i < sz(g[v]); ++i) {
    string& s = g[v][i].second;
    for (int j = 0; j < sz(s); ++j) {
      H new_back = (st.back() * p + to_h(s[j])) % m;
      st.pb(new_back);
    }
    dfs(g[v][i].first, cur_sz);
  }

  while (sz(st) != old_sz) {
    st.pop_back();
  }
}

int main() {
  //freopen(PATH"in.txt", "r", stdin);
  //ios_base::sync_with_stdio(false);

  ppow[0] = mp(1ll, 1ll);
  for (int i = 0; i < M; ++i) {
    ppow[i + 1] = (ppow[i] * p) % m;
  }

  int n;
  cin >> n;
  string s;
  for (int i = 1, p; i < n; ++i) {
    cin >> p >> s;
    reduce(s);
    g[p - 1].pb(mp(i, s));
  }
  cin >> s;
  reduce(s);
  goal_h = calc_h(s);

  goal_sz = sz(s);
  dfs(0, 1);
  cout << ans << endl;

  return 0;
}


#pragma comment(linker,"/stack:67108864")
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <sstream>
#include <fstream>
#include <climits>
#include <cfloat>
#include <functional>
#include <ctime>
#include <numeric>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <utility>
#include <bitset>
#include <string>
#include <list>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <typeinfo>
#include <cassert>
#include <ctime>

using namespace std;

#define forn(i, n) for(int i = 0; i < int(n); ++i)
#define forn1(i, n) for(int i = 1; i <= int(n); ++i)
#define ford(i, n) for(int i = int(n) - 1; i >= 0; --i)
#define ford1(i, n) for(int i = int(n); i > 0; --i)
#define foran(i, a, n) for(int i = int(a); i <= int(n); ++i)

#define mp make_pair
#define pb push_back
#define sqr(x) (x)*(x)
#define all(v) (v).begin(),(v).end()

typedef long double ld;
typedef long long ll;

const ld EPS = 1e-9;
const ld PI = 3.1415926535897932384626433832795;
const ll INF = 1000 * 1000 * 1000 + 9;
const int NMAX = 100500 + 300;

int n;
vector <int> g[NMAX];
string s[NMAX];
vector <char> ch;
string start;

inline bool read()
{
    if (!(cin >>n))
        return false;
    forn1(i, n - 1)
    {
        int v;
        string cur;
        scanf("%d", &v);
        cin >>cur;
        v--;
        g[v].pb(i);
        s[i] = cur;
    }
    cin >>start;
    ch.clear();
    forn(i, start.size())
        ch.push_back(start[i]);
    return true;
}

vector<int> pi;

inline bool pref (int pos) {
    pi.pb(0);
    if (pos == 0)
        return 0;
    {
        int j = pi[pos - 1];
        while (j > 0 && ch[pos] != ch[j])
            j = pi[j - 1];
        if (ch[pos] == ch[j])  ++j;
        pi[pos] = j;
    }
    return (start.size() == pi[pos]);
}

bool used[NMAX];
int ans;

inline void show(vector <char> ch)
{
    forn(i, ch.size())
        cerr <<ch[i];
    cerr <<endl;
}

inline void show(vector <int> pi)
{
    forn(i, pi.size())
        cerr <<pi[i];
    cerr <<endl;
}

inline void dfs(int i)
{
    if (used[i])
        return;
    forn(j, s[i].size())
    {
        //cerr <<ans <<' '; show(ch); show(pi);
        ch.pb(s[i][j]);
        ans += pref(ch.size() - 1);
    }
    forn(j, g[i].size())
    {
        int v2 = g[i][j];
        dfs(v2);
    }
    forn(j, s[i].size())
    {
        //show(ch);
        ch.pop_back();
        pi.pop_back();
    }
}

inline void solve()
{
    pi.clear();
    ch.pb('#');
    //show(ch);
    forn(i, ch.size())
    {
        pref(i);
    }
    ans = 0;
    dfs(0);
    cout <<ans <<endl;
}

int main()
{
    ifstream ifile("input.txt");
    if (ifile) {
        freopen("input.txt", "rt", stdin);
    }
#ifdef ONLINE_JUDGE
    if (ifile) {
        freopen("output.txt","wt",stdout);
    }
#endif
    while(read())
        solve();
    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <vector>

using namespace std;

#define int64 long long

int n;
vector< int > v[100001];
string st[100001];
int fail[300001];
string t;
int64 res = 0;

void dfs( int a, int match ) {
    for( int i = 0; i < v[a].size(); i++ ) {
        int to = v[a][i];
        int m = match;
        for( int j = 0; j < st[to].length(); j++ ) {
            while( m != -1 && st[to][j] != t[m] ) {
                if( m == 0 )    m = -1;
                else            m = fail[m - 1];
            }
            m++;
            if( m == t.length() ) {
                res++;
                m = fail[m - 1];
            }
        }
        dfs( to, m );
    }
}

int main() {
    cin >> n;
    for( int i = 1; i < n; i++ ) {
        int p;
        cin >> p >> st[i];
        v[p - 1].push_back( i );
    }
    cin >> t;
    fail[0] = 0;
    for( int i = 1; i < t.length(); i++ ) {
        int cur = i - 1;
        while( cur != -1 && t[fail[cur]] != t[i] ) {
            if( cur == 0 ) {
                fail[i] = 0;
                goto lp;
            }
            cur = fail[cur];
        }
        fail[i] = fail[cur] + 1;
lp:     ;
    }
    dfs( 0, 0 ) ;
    cout << res << endl;
    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <vector>

using namespace std;

#define int64 long long

int n;
vector< int > v[100001];
string st[100001];
int fail[300002];
string t;
int64 res = 0;

void dfs( int a, int match ) {
    for( int i = 0; i < v[a].size(); i++ ) {
        int to = v[a][i];
        int m = match;
        for( int j = 0; j < st[to].length(); j++ ) {
            while( m != -1 && st[to][j] != t[m] ) m = fail[m];
            if( ++m == t.length() ) {
                res++;
                m = fail[m];
            }
        }
        dfs( to, m );
    }
}

int main() {
    cin >> n;
    for( int i = 1; i < n; i++ ) {
        int p;
        cin >> p >> st[i];
        v[p - 1].push_back( i );
    }
    cin >> t;
    int cur = fail[0] = -1;
    for( int i = 1; i <= t.length(); i++ ) {
        while( cur != -1 && t[cur] != t[i - 1] ) cur = fail[cur];
        fail[i] = ++cur;
    }
    dfs( 0, 0 ) ;
    cout << res << endl;
    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <vector>

using namespace std;

#define int64 long long

int n;
vector< int > v[100001];
string st[100001];
int fail[300001];
string t;
int64 res = 0;

void dfs( int a, int match ) {
    for( int i = 0; i < v[a].size(); i++ ) {
        int to = v[a][i];
        int m = match;
        for( int j = 0; j < st[to].length(); j++ ) {
            while( m != -1 && st[to][j] != t[m] ) {
                if( m == 0 )    m = -1;
                else            m = fail[m - 1];
            }
            m++;
            if( m == t.length() ) {
                res++;
                m = fail[m - 1];
            }
        }
        dfs( to, m );
    }
}

int main() {
    cin >> n;
    for( int i = 1; i < n; i++ ) {
        int p;
        cin >> p >> st[i];
        v[p - 1].push_back( i );
    }
    cin >> t;
    fail[0] = -1;
    for( int i = 1; i < t.length(); i++ ) {
        int cur = i - 1;
        while( cur != -1 && t[fail[cur]] != t[i] ) cur = fail[cur];
        fail[i] = (cur == -1 ? 0 : fail[cur] + 1);
    }
    dfs( 0, 0 ) ;
    cout << res << endl;
    return 0;
}


#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <algorithm>
#include <functional>
#include <cmath>
#include <sstream>
#include <utility>
#include <ctime>
#include <memory.h>
#include <cctype>
#include <cstdlib>
#include <limits>

using namespace std;

#pragma comment (linker, "/STACK:128000000")

#define forn(j, n) for(j=1; j<=n; j++)
#define sz size()
#define pb push_back
#define o 1000000007
#define fors(w, s) for(set <int> ::iterator w=s.begin(); w!=s.end(); w++)
#define pii pair<int, int>
#define uns unsigned
#define vi vector <int>
#define vvi vector <vi>

int j, n, k, lg, q, q1, q2, res1, res2, x, m, i, pf[400500], ans;
vvi v;
string t[100500], ss, s=" ";

void dfs (int i)
{
	int l=s.size(), h, j;
	s+=t[i];
	for (j=l; j<s.sz; j++)
	{
		h=j-1;
		while (s[pf[h]+1]!=s[j] & h>0)
		{
			h=pf[h];
		}
		if (h==0)
		{
			pf[j]=0;
		}
		else
		{
			pf[j]=pf[h]+1;
			if (pf[j]==ss.sz)
			{
				ans++;
			}
		}
	}
	for (j=0; j<v[i].sz; j++)
	{
		dfs (v[i][j]);
	}
	s.erase(l);
}

int main()
{
    //freopen ("input.txt","r",stdin); freopen ("output.txt","w",stdout);
    //freopen ("nosimple.in","r",stdin); freopen ("nosimple.out","w",stdout);
	scanf ("%d\n", &n);
	v.resize(n+5);
	forn (j, n-1)
	{
		scanf ("%d ", &x);
		v[x].pb(j+1);
		cin >> t[j+1];
	}
	cin >> ss;
	s+=ss;
	s+=' ';
	pf[1]=0;
	for (j=2; j<s.sz; j++)
	{
		i=j-1;
		while (s[pf[i]+1]!=s[j] & i>0)
		{
			i=pf[i];
		}
		if (i==0)
		{
			pf[j]=0;
		}
		else
		{
			pf[j]=pf[i]+1;
		}
	}
	dfs(1);
	printf ("%d", ans);
    return 0;
}


#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <list>
#include <complex>
#include <queue>
#pragma comment(linker, "/STACK:266777216")
using namespace std;

#define assert(f) { if(!(f)) { fprintf(stderr,"Assertion failed: "); fprintf(stderr,#f); fprintf(stderr,"\n"); exit(1); } }

typedef long long LL;
typedef unsigned long long ULL;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<double> VD;
typedef pair<double,double> PDD;

const int inf=1000000000;
const LL INF=LL(inf)*inf;
const double eps=1e-9;
const double PI=2*acos(0.0);
#define bit(n) (1<<(n))
#define bit64(n) ((LL(1))<<(n))
#define pb push_back
#define sz size()
#define mp make_pair
#define cl clear()
#define all(a) (a).begin(),(a).end()
#define fill(ar,val) memset((ar),(val),sizeof (ar))
#define MIN(a,b) {if((a)>(b)) (a)=(b);}
#define MAX(a,b) {if((a)<(b)) (a)=(b);}
#define sqr(x) ((x)*(x))
#define X first
#define Y second

//clock_t start=clock();
//fprintf(stderr,"time=%.3lfsec\n",0.001*(clock()-start));

#define N 111111
#define L 333333

char buf[L];
VI a[N];
int p[N];
string s[N];
int cnt;
int d[L];
int par[L][20];
LL HASH[L][20];

void dfs(int u,int du)
{
	int idu=cnt;
	d[cnt]=du;
	for(int i=0;i<a[u].sz;i++)
	{
		int v=a[u][i];
		int prev = idu;
		for(int j=0;j<s[v].sz;j++)
		{
			cnt++;
			d[cnt]=du+j+1;
			par[cnt][0]=prev;
			HASH[cnt][0]=s[v][j];
			prev=cnt;
		}
		dfs(v,du+s[v].sz);
	}
}

#define MOD 1415926579
#define P 157LL
int pw[L]={1};

int main()
{
#ifdef MY_SIDE
	freopen("e.in","r",stdin);
#endif
	int n;
	for(n=1;n<L;n++)
		pw[n] = P * pw[n-1] % MOD;
	while(scanf("%d",&n)>0)
	{
		fill(p,0);
		for(int i=1;i<=n;i++) a[i].cl;
		for(int i=2;i<=n;i++)
		{
			scanf("%d %s",p+i,buf);
			a[p[i]].pb(i);
			s[i]=buf;
		}
		scanf("%s",buf);
		int tn = strlen(buf);
		VI bin;
		for(int i=0;i<20;i++)
			if(tn & bit(i)) bin.pb(i);
		int hasht=0;
		for(int i=0;i<tn;i++)
			hasht = (P * hasht + buf[i]) % MOD;
		cnt=1;
		dfs(1,0);
		for(int h=0;bit(h)<=cnt;h++)
			for(int u=1;u<=cnt;u++)
			{
				int pu=par[u][h];
				par[u][h+1]=par[pu][h];
				HASH[u][h+1] = (HASH[u][h] + LL(pw[bit(h)]) * HASH[pu][h]) % MOD;
			}
		int ans=0;
		for(int u=1;u<=cnt;u++)
			if(d[u]>=tn) {
				int v=u;
				int path=0;
				int cur_hash=0;
				for(int i=0;i<bin.sz;i++)
				{
					int h = bin[i];
					cur_hash = (cur_hash + LL(pw[path]) * HASH[v][h]) % MOD;
					v = par[v][h];
					path += bit(h);
				}
				if(cur_hash==hasht) ans++;
			}
		printf("%d\n",ans);
	}
	return 0;
}


#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <list>
#include <complex>
#include <queue>
#pragma comment(linker, "/STACK:266777216")
using namespace std;

#define assert(f) { if(!(f)) { fprintf(stderr,"Assertion failed: "); fprintf(stderr,#f); fprintf(stderr,"\n"); exit(1); } }

typedef long long LL;
typedef unsigned long long ULL;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<double> VD;
typedef pair<double,double> PDD;

const int inf=1000000000;
const LL INF=LL(inf)*inf;
const double eps=1e-9;
const double PI=2*acos(0.0);
#define bit(n) (1<<(n))
#define bit64(n) ((LL(1))<<(n))
#define pb push_back
#define sz size()
#define mp make_pair
#define cl clear()
#define all(a) (a).begin(),(a).end()
#define fill(ar,val) memset((ar),(val),sizeof (ar))
#define MIN(a,b) {if((a)>(b)) (a)=(b);}
#define MAX(a,b) {if((a)<(b)) (a)=(b);}
#define sqr(x) ((x)*(x))
#define X first
#define Y second

//clock_t start=clock();
//fprintf(stderr,"time=%.3lfsec\n",0.001*(clock()-start));

#define N 111111
#define L 333333

char buf[L];
VI a[N];
int p[N];
string s[N];
int cnt;
int d[L];
int par[L][20];
LL hash[L][20];

void dfs(int u,int du)
{
	int idu=cnt;
	d[cnt]=du;
	for(int i=0;i<a[u].sz;i++)
	{
		int v=a[u][i];
		int prev = idu;
		for(int j=0;j<s[v].sz;j++)
		{
			cnt++;
			d[cnt]=du+j+1;
			par[cnt][0]=prev;
			hash[cnt][0]=s[v][j];
			prev=cnt;
		}
		dfs(v,du+s[v].sz);
	}
}

#define MOD 1415926579
#define P 157LL
int pw[L]={1};

int main()
{
#ifdef MY_SIDE
	freopen("e.in","r",stdin);
#endif
	int n;
	for(n=1;n<L;n++)
		pw[n] = P * pw[n-1] % MOD;
	while(scanf("%d",&n)>0)
	{
		fill(p,0);
		for(int i=1;i<=n;i++) a[i].cl;
		for(int i=2;i<=n;i++)
		{
			scanf("%d %s",p+i,buf);
			a[p[i]].pb(i);
			s[i]=buf;
		}
		scanf("%s",buf);
		int tn = strlen(buf);
		VI bin;
		for(int i=0;i<20;i++)
			if(tn & bit(i)) bin.pb(i);
		int hasht=0;
		for(int i=0;i<tn;i++)
			hasht = (P * hasht + buf[i]) % MOD;
		cnt=1;
		dfs(1,0);
		for(int h=0;bit(h)<=cnt;h++)
			for(int u=1;u<=cnt;u++)
			{
				int pu=par[u][h];
				par[u][h+1]=par[pu][h];
				hash[u][h+1] = (hash[u][h] + LL(pw[bit(h)]) * hash[pu][h]) % MOD;
			}
		int ans=0;
		for(int u=1;u<=cnt;u++)
			if(d[u]>=tn) {
				int v=u;
				int path=0;
				int cur_hash=0;
				for(int i=0;i<bin.sz;i++)
				{
					int h = bin[i];
					cur_hash = (cur_hash + LL(pw[path]) * hash[v][h]) % MOD;
					v = par[v][h];
					path += bit(h);
				}
				if(cur_hash==hasht) ans++;
			}
		printf("%d\n",ans);
	}
	return 0;
}


#if 1
#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <iomanip>
#include <sstream>
#include <numeric>
#include <string>
#include <vector>
#include <bitset>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <list>

using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int, int> pii;

const LD eps = 1e-9;
//const LD pi = acos(-1.0);
const LL inf = 1e+9;

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

#define dbg(x) { cerr << #x << " = " << x << endl; }

// extended template
#pragma comment(linker, "/STACK:36777216")
typedef vector<int> vi;
typedef vector<vi> vvi;

#define forn(i, n) for (int i = 0; i < n; ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()

template<typename T> istream & operator >> (istream &, vector<T> &);
template<typename T> ostream & operator << (ostream &, const vector<T> &);

#define START clock_t _clock = clock();
#define END cerr << endl << "time: " << (clock() - _clock) / LD(CLOCKS_PER_SEC) << endl;

#define NAME "problem"

void data_read(int &n, vector<string> &v, vvi &g)
{
    cin >> n;
    g.resize(n + 1);
    v.resize(n + 1);

    g[0].pb(1);
    forn(i, n - 1)
    {
        int p;
        cin >> p >> v[i + 2];
        g[p].pb(i + 2);
    }

    cin >> v[1];
    v[1] += '#';

    //forn(i, v.size())
    //    cout << i << ": " << v[i] << endl;
    //cout << endl;
    //forn(i, g.size())
    //    cout << i << ": " << g[i] << endl;
}

vi prefix_function(const string &s)
{
    const int n = s.length();
    vector<int> pi(n);
    for (int i = 1; i < n; ++i)
    {
        int t = pi[i - 1];
        while (t > 0 && s[i] != s[t])
            t = pi[t - 1];
        if (s[i] == s[t])
            ++t;
        pi[i] = t;
    }
    return pi;
}

int res = 0;
void dfs(int from, vi &pi, string &s, const vvi &g, const vector<string> &v, vvi &pr, int need_length)
{
    int pos = pi.size();
    forn(k, g[from].size())
    {
        int to = g[from][k];
        s += v[to];

        pi.resize(pi.size() + v[to].length());
        for (int i = pos; i < s.length(); ++i)
        {
            int t = pi[i - 1];
            while (t > 0 && s[i] != s[t])
                t = pi[t - 1];
            if (s[i] == s[t])
                ++t;
            pi[i] = t;
        }

        pr[to].resize(v[to].length());
        forn(i, v[to].length())
        {
            pr[to][i] = pi[i + pos];
            if (pr[to][i] == need_length)
                ++res;
        }

        dfs(to, pi, s, g, v, pr, need_length);

        pi.resize(pos);
        s.resize(pos);
    }
}

void solve()
{
    int n;
    vector<string> v;
    vvi g;
    data_read(n, v, g);

    vvi pr(n + 1);
    string s = v[1];
    vi pi = prefix_function(s);
    pr[1] = pi;

    const int maxlen = 3e5 + 100;
    pi.reserve(maxlen);
    s.reserve(maxlen);
    dfs(1, pi, s, g, v, pr, int(v[1].length()) - 1);

    //cout << endl;
    //cerr << "pr" << endl;
    //forn(i, v.size())
    //    cout << i << ": " << pr[i] << endl;
    //cout << endl;

    cout << res << endl;
}

int main()
{
    START
    // freopen(NAME ".in", "r", stdin); freopen(NAME ".out", "w", stdout);
    // freopen("input.txt", "r", stdin);

    ios_base::sync_with_stdio(false);

    solve();

    END
    return 0;
}
/*******************************************
7
1 ab
5 bacaba
1 abacaba
2 aca
5 ba
2 ba
aba
*******************************************/

template<typename T> istream & operator >> (istream &is, vector<T> &v)
{
    forn(i, v.size())
        is >> v[i];
    return is;
}
template<typename T> ostream & operator << (ostream &os, const vector<T> &v)
{
    forn(i, v.size())
        os << v[i] << " ";
    return os;
}
#endif


//#ifdef _MONYURA_
#pragma comment(linker,"/STACK:256000000")
//#endif

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <bitset>
#include <memory>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <list>
#include <string>
#include <cstring>
#include <fstream>
#include <functional>
#include <stack>
#include <complex>
#include <wchar.h>
#include <wctype.h>
#include <cmath>
#include <queue>
#include <ctime>
#include <numeric>

using namespace std;

template<typename T> T mabs(const T &a){ return a<0?-a:a;}
#define rep(x,y,z) for(int x=(y),e##x=(z);x<e##x;x++)
#define SQR(x) ((x)*(x))
#define all(c) (c).begin(), (c).end()

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef unsigned int ui;
typedef short int si;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

const double PI = acos(-1.0);

const int MAX_N = 300300;

int mod1 = 1e9+9;
int mod2 = 999999937;
ll c1 = 17531;
ll c2 = 19;

ll c1Pow;
ll c2Pow;

pll Hash[MAX_N];

pll calcHash(int l,int r)
{
	pll res = Hash[r];
	res.first = (res.first - c1Pow*Hash[l-1].first) % mod1;
	res.second = (res.second - c2Pow*Hash[l-1].second) % mod2;
	if (res.first<0) res.first+=mod1;
	if (res.second<0) res.second+=mod2;
	return res;
}

struct vert
{
	vector<int> inc;
	vector<char*> str;
	int d;
} g[100100];

pll goalHash;
int len;
int res;

void dfs(int k,int depth)
{
	g[k].d = depth;
	rep(i,0,g[k].inc.size())
	{
		depth = g[k].d;
		char *str = g[k].str[i];
		for (int j=0;str[j];++j,++depth)
		{
			Hash[depth].first = (Hash[depth-1].first*c1 + str[j])%mod1;
			Hash[depth].second = (Hash[depth-1].second*c2 + str[j])%mod2;
			if (depth>=len && calcHash(depth-len+1,depth)==goalHash)
				++res;
		}
		dfs(g[k].inc[i],depth);
	}
}

void run()
{
	char BUFF[2*MAX_N + 3]={0};
	int n;
	cin>>n;
	char *curr = BUFF;
	rep(i,0,n-1)
	{
		int p;
		scanf("%d %s",&p,curr);
		--p;
		g[p].inc.push_back(i+1);
		g[p].str.push_back(curr);
		curr+=strlen(curr)+1;
	}
	char str[MAX_N];
	scanf("%s",str);
	len = strlen(str);
	c1Pow  = c2Pow = 1;
	rep(i,0,len)
	{
		c1Pow = (c1Pow*c1)%mod1;
		c2Pow = (c2Pow*c2)%mod2;
	}
	goalHash=pll(0,0);
	rep(i,0,len)
	{
		goalHash.first = (goalHash.first*c1 + str[i])%mod1;
		goalHash.second = (goalHash.second*c2 + str[i])%mod2;
	}
	res = 0;
	Hash[0]=pll(0,0);
	dfs(0,1);
	printf("%d\n",res);
}

int main()
{
#ifdef _MONYURA_
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	time_t st=clock();
#else
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
#endif
	run();
#ifdef _MONYURA_
	printf( "=============\n");
	printf("Time: %.2lf sec\n",(clock()-st)/double(CLOCKS_PER_SEC));
#endif

	return 0;
}


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read().split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = ['']
    ea = edge_str.append
    for i in xrange(1, n):
        p, t = inp[i].split()
        G[int(p) - 1].append(i)
        ea(t)
    l = [len(edge_str[i]) for i in xrange(n)]
    ptr = '^' + inp[n]
    ptrl = len(ptr)
    f = ff(ptr)
    stack = [(0, 1)]
    ans = j = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            if c == ptr[j]:
                j = j + 1
                if j == ptrl:
                    ans = ans + 1
                    j = f[j - 1] + 1
            else:
                j = f[j]
            if not j:
                j = j + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read().split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = ['']
    ea = edge_str.append
    for i in xrange(1, n):
        p, t = inp[i].split()
        G[int(p) - 1].append(i)
        ea(t)
    l = [len(edge_str[i]) for i in xrange(n)]
    ptr = '^' + inp[n]
    ptrl = len(ptr) - 1
    f = ff(ptr)
    stack = [(0, 1)]
    ans = j = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            while j and c != ptr[j]:
                j = f[j]
            if j == ptrl:
                ans = ans + 1
                j = f[j]
            j = j + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read(0).split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = {'1': ''}
    for i in xrange(1, n):
        p, t = inp[i].split(' ')
        G[p].append(str(i+1))
        edge_str[str(i+1)] = t
    ptr = inp[n]
    ptrl = len(ptr)
    f = ff('^' + ptr)
    stack = [('1', 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            while c != ptr[j]:
                if not j:
                    break
                j = f[j]
            else:
                j = j + 1
                if j == ptrl:
                    ans = ans + 1
                    j = f[j]
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[1:], 1):
        j = f[i]
        while s[j] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    alphas = ''.join(chr(ord('a') + c) for c in xrange(26))
    ret = [{c: 0 for c in alphas}]
    ret[0][s[0]] = 1
    for i, c in enumerate(s[1:], 1):
        d = {a: [ret[f[i]][a], i+1][c == a] for a in alphas}
        ret.append(d)
    ret.append({c: ret[f[-1]][c] for c in alphas})
    return ret
def main():
    RL = stdin.readline
    n = int(RL())
    G = defaultdict(list)
    edge_str = ['', '']
    for i in xrange(2, n+1):
        p, t = RL().split()
        G[int(p)].append(i)
        edge_str.append(t)
    ptr = RL().strip()
    ptrl = len(ptr)
    f = ff(ptr)
    stack = [(1, 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            j = f[j][c]
            if j == ptrl:
                ans = ans + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read().split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = ['']
    ea = edge_str.append
    for i in xrange(1, n):
        p, t = inp[i].split()
        G[int(p) - 1].append(i)
        ea(t)
    ptr = inp[n]
    ptrl = len(ptr)
    f = ff('^' + ptr)
    stack = [(0, 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            while c != ptr[j]:
                if not j:
                    break
                j = f[j]
            else:
                j = j + 1
                if j == ptrl:
                    ans = ans + 1
                    j = f[j]
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read().split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = ['']
    ea = edge_str.append
    for i in xrange(1, n):
        p, t = inp[i].split()
        G[int(p) - 1].append(i)
        ea(t)
    l = [len(edge_str[i]) for i in xrange(n)]
    ptr = '^' + inp[n]
    ptrl = len(ptr) - 1
    f = ff(ptr)
    stack = [(0, 1)]
    ans = j = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            while c != ptr[j]:
                if not j:
                    j = 1
                    break
                j = f[j]
            else:
                if j == ptrl:
                    ans = ans + 1
                    j = f[j]
                j = j + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[2:], 1):
        j = f[i]
        while s[j+1] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    return f
def main():
    inp = stdin.read().split('\n')
    n = int(inp[0])
    G = defaultdict(list)
    edge_str = {'1': ''}
    for i in xrange(1, n):
        p, t = inp[i].split(' ')
        G[p].append(str(i+1))
        edge_str[str(i+1)] = t
    ptr = inp[n]
    ptrl = len(ptr)
    f = ff('^' + ptr)
    stack = [('1', 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            while c != ptr[j]:
                if not j:
                    break
                j = f[j]
            else:
                j = j + 1
                if j == ptrl:
                    ans = ans + 1
                    j = f[j]
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
import re

stdin = open('test1.txt') #<---------------------

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
findall = regex.findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

class AhoNode:
    ''' Вспомогательный класс для построения дерева
    '''
    def __init__(self):
        self.goto = {}
        self.out = []
        self.fail = None

def aho_create_forest(patterns):
    '''Создать бор - дерево паттернов
    '''
    root = AhoNode()

    for path in patterns:
        node = root
        for symbol in path:
            node = node.goto.setdefault(symbol, AhoNode())
        node.out.append(path)
    return root

def aho_create_statemachine(patterns):
    '''Создать автомат Ахо-Корасика.
    Фактически создает бор и инициализирует fail-функции
    всех узлов, обходя дерево в ширину.
    '''
    # Создаем бор, инициализируем
    # непосредственных потомков корневого узла
    root = aho_create_forest(patterns)
    queue = []
    for node in root.goto.itervalues():
        queue.append(node)
        node.fail = root

    # Инициализируем остальные узлы:
    # 1. Берем очередной узел (важно, что проход в ширину)
    # 2. Находим самую длинную суффиксную ссылку для этой вершины - это и будет fail-функция
    # 3. Если таковой не нашлось - устанавливаем fail-функцию в корневой узел
    while len(queue) > 0:
        rnode = queue.pop(0)

        for key, unode in rnode.goto.iteritems():
            queue.append(unode)
            fnode = rnode.fail
            while fnode != None and not fnode.goto.has_key(key):
                fnode = fnode.fail
            unode.fail = fnode.goto[key] if fnode else root
            unode.out += unode.fail.out

    return root

def aho_find_all(s, root):
    '''Находит все возможные подстроки из набора паттернов в строке.
    '''
    node = root
    counter = 0

    for i in xrange(len(s)):
        while node != None and not node.goto.has_key(s[i]):
            node = node.fail
        if node == None:
            node = root
            continue
        node = node.goto[s[i]]
        for pattern in node.out:
            counter += 1
    return counter

patterns = [pattern]
root = aho_create_statemachine(patterns)

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    start = 0
    if len(word)>length:
        counter += aho_find_all(word, root)
#        counter += len(findall(word))
    reminder = word[-length:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

first = pattern[0]
last = pattern[1:]
findall = re.compile(first +'(?=' + last[:100] + ')').findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    try:
        reminder, index = search.pop()
        word = reminder + words[index]
        counter += len(findall(word) or [])
        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])
    except:
        break

print counter


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def KnuthMorrisPratt(text, pattern):
    count = 0
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in range(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift

    startPos = 0
    matchLen = 0
    for c in text:
        while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
            startPos += shifts[matchLen]
            matchLen -= shifts[matchLen]
        matchLen += 1
        if matchLen == length:
            count += 1
    return count

def task():
    from sys import stdin
    from collections import defaultdict
    import re

    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    #regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
    #findall = regex.findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    shifts = KnuthMorrisPrattPattern(pattern)

    while True:
        if not search:
            break
        reminder, index = search.pop()
        word = reminder + words[index]
        if len(word) > length:
            startPos = 0
            matchLen = 0
            cl = 0
            for c in word:
                while matchLen == length + 1 or matchLen >= 0 and pattern[matchLen] != c:
                    startPos += shifts[matchLen]
                    matchLen -= shifts[matchLen]
                matchLen += 1
                if matchLen == length + 1:
                    cl += 1
            counter += cl

        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])

    print counter

task()


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from sys import stdin, exit

def KnuthMorrisPratt(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def task():
    from sys import stdin
    from collections import defaultdict
    import re

    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        try:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        except ValueError:
            pattern = value[:-1]

    regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
    findall = regex.findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    shifts = KnuthMorrisPratt(pattern)

    while True:
        if not search:
            break
        reminder, index = search.pop()
        word = reminder + words[index]
        if len(word) > length:
            startPos = 0
            matchLen = 0
            for c in word:
                while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
                    startPos += shifts[matchLen]
                    matchLen -= shifts[matchLen]
                matchLen += 1
                if matchLen == length:
                    counter += 1

            #counter += len(findall(word))

        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])

    print counter

task()


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def KnuthMorrisPratt(text, pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in range(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift

    startPos = 0
    matchLen = 0
    for c in text:
        while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
            startPos += shifts[matchLen]
            matchLen -= shifts[matchLen]
        matchLen += 1
        if matchLen == length:
            yield startPos

def task():
    from sys import stdin
    from collections import defaultdict
    import re

    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        try:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        except ValueError:
            pattern = value[:-1]

    regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
    findall = regex.findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    shifts = KnuthMorrisPrattPattern(pattern)

    while True:
        if not search:
            break
        reminder, index = search.pop()
        word = reminder + words[index]
        if len(word) > length:
            startPos = 0
            matchLen = 0
            for c in word:
                while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
                    startPos += shifts[matchLen]
                    matchLen -= shifts[matchLen]
                matchLen += 1
                if matchLen == length:
                    counter += 1
                    break
            #counter += len(findall(word))

        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])

    print counter

task()


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
findall = regex.findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

class AhoNode:
    ''' Вспомогательный класс для построения дерева
    '''
    def __init__(self):
        self.goto = {}
        self.out = []
        self.fail = None

def aho_create_forest(patterns):
    '''Создать бор - дерево паттернов
    '''
    root = AhoNode()

    for path in patterns:
        node = root
        for symbol in path:
            node = node.goto.setdefault(symbol, AhoNode())
        node.out.append(path)
    return root

def aho_create_statemachine(patterns):
    '''Создать автомат Ахо-Корасика.
    Фактически создает бор и инициализирует fail-функции
    всех узлов, обходя дерево в ширину.
    '''
    # Создаем бор, инициализируем
    # непосредственных потомков корневого узла
    root = aho_create_forest(patterns)
    queue = []
    for node in root.goto.itervalues():
        queue.append(node)
        node.fail = root

    # Инициализируем остальные узлы:
    # 1. Берем очередной узел (важно, что проход в ширину)
    # 2. Находим самую длинную суффиксную ссылку для этой вершины - это и будет fail-функция
    # 3. Если таковой не нашлось - устанавливаем fail-функцию в корневой узел
    while len(queue) > 0:
        rnode = queue.pop(0)

        for key, unode in rnode.goto.iteritems():
            queue.append(unode)
            fnode = rnode.fail
            while fnode != None and not fnode.goto.has_key(key):
                fnode = fnode.fail
            unode.fail = fnode.goto[key] if fnode else root
            unode.out += unode.fail.out

    return root

def aho_find_all(s, root):
    '''Находит все возможные подстроки из набора паттернов в строке.
    '''
    node = root
    counter = 0

    for i in xrange(len(s)):
        while node != None and not node.goto.has_key(s[i]):
            node = node.fail
        if node == None:
            node = root
            continue
        node = node.goto[s[i]]
        for pattern in node.out:
            counter += 1
    return counter

patterns = [pattern]
root = aho_create_statemachine(patterns)

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    start = 0
    if len(word)>length:
        counter += aho_find_all(word, root)
#        counter += len(findall(word))
    reminder = word[-length:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


from sys import stdin
from collections import defaultdict
import re

#stdin = open('test1.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:]))

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    counter += len(regex.findall(word))
    reminder = word[-last_index:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем
дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева
будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v)
и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны
строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя
вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv,
которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках
пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    return pi

def compute_matches(pi, pattern):
    from string import ascii_lowercase

    r = [{c: 0 for c in ascii_lowercase}]
    r[0][pattern[0]] = 1
    for i, p in enumerate(pattern[1:], start=1):
        r.append({c: [r[pi[i-1]][c], i + 1][p == c] for c in ascii_lowercase})
    r.append({c: r[pi[-1]][c] for c in ascii_lowercase})
    return r

#@profile
def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []
    pattern = ''

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, child) for child in tree[-1]]

    pi = compute_prefix_function(pattern)
    cpi = compute_matches(pi, pattern)
    cache = {}

    while search:
        matchLen, index = search.pop()
        cl = 0
        key = (matchLen, words[index])
        if key in cache:
            matchLen, cl = cache[key]
        else:
            for c in words[index]:
                matchLen = cpi[matchLen][c]
                if matchLen == length:
                    cl += 1
            cache[key] = matchLen, cl
        counter += cl
        search.extend([(matchLen, child) for child in tree[index]])

    print counter

task()


try:
    from sys import stdin
    from collections import defaultdict
    import re

    n = int(stdin.readline())

    tree = defaultdict(list)
    words = []
    words_append = words.append

    for index,value in enumerate(stdin.readlines()):
        try:
            parent_string,word = value.split()
            parent = int(parent_string) - 2
            words_append(word)
            tree[parent].append(index)
        except ValueError:
            pattern = value[:-1]

    regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:]))
    findall = regex.findall
    length = len(pattern) - 1
    counter = 0
    search = [('', child) for child in tree[-1]]
    extend = search.extend
    pop = search.pop

    while True:
        if not search:
            break
        try:
            reminder, index = pop()
            word = reminder + words[index]
            counter += len(findall(word) or [])
            reminder = word[-length:]
            extend([(reminder, child) for child in tree[index]])
        except:
            break

    print counter
except:
    print -1


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

findall = re.compile('%s(?=%s)' % (pattern[0], pattern[1:])).findall
try:
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    while True:
        if not search:
            break
        try:
            reminder, index = search.pop()
            word = reminder + words[index]
            counter += len(findall(word) or [])
            reminder = word[-length:]
            search.extend([(reminder, child) for child in tree[index]])
        except:
            break

    print counter
except:
    print -1


from sys import stdin
from collections import defaultdict
import re

#stdin = open('tree.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:]))

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    counter += len(regex.findall(reminder + words[index]))
    reminder = word[-last_index:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


################################
#  Test submit of nwin code    #
################################
from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[1:], 1):
        j = f[i]
        while s[j] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    alphas = ''.join(chr(ord('a') + c) for c in xrange(26))
    ret = [{c: 0 for c in alphas}]
    ret[0][s[0]] = 1
    for i, c in enumerate(s[1:], 1):
        d = {a: [ret[f[i]][a], i+1][c == a] for a in alphas}
        ret.append(d)
    ret.append({c: ret[f[-1]][c] for c in alphas})
    return ret
def main():
    RL = stdin.readline
    n = int(RL())
    G = defaultdict(list)
    edge_str = ['', '']
    for i in xrange(2, n+1):
        p, t = RL().split()
        G[int(p)].append(i)
        edge_str.append(t)
    ptr = RL().strip()
    ptrl = len(ptr)
    f = ff(ptr)
    stack = [(1, 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            j = f[j][c]
            if j == ptrl:
                ans = ans + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from datetime import datetime as dt
from collections import defaultdict
import gc

gc.disable()

#stdin = open('tree.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append((parent,word))
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = tree[-1]
reminders = {-1:''}

while True:
    if not search:
        break
    index = search.pop()
    search.extend(tree[index])
    parent, word = words[index]
    full_string = reminders[parent] + word
    start = 0
    while True:
        start = full_string.find(pattern, start) + 1
        if start == 0:
            break
        else:
            counter += 1

    reminders[index] = full_string[-last_index:]

print counter


from sys import stdin
from collections import defaultdict
n = int(stdin.readline())
tree = defaultdict(list)
words = []
none = [None]
for index in xrange(n-1):
    parent_string,word = stdin.readline()[:-1].split(' ')
    parent = int(parent_string) - 2
    words.append((parent,word))
    tree[parent].append(index)
pattern = list(stdin.readline().replace('\n',''))
last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]
counter = 0
search = tree[-1]
found = defaultdict(list)
while True:
    if not search:
        break
    index = search.pop()
    search.extend(tree[index])
    parent, word = words[index]
    anchors = list(found[parent])
    candidate = False
    for w in word:
        for i,anchor in enumerate(anchors):
            if w == pattern[anchor]:
                if anchor == last_index:
                    counter += 1
                    anchors.remove(anchor)
                else:
                    anchors[i] += 1
            else:
                anchors.remove(anchor)
        if candidate:
            candidate = False
            if w == second_char:
                anchors.append(2)
        if w == first_char:
            candidate = True
    if candidate:
        anchors.append(1)
    found[index] = anchors
print counter


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

findall = re.compile('%s(?=%s)' % (pattern[0], pattern[1:20])).findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    if len(word)>length:
        counter += len(findall(word))
    reminder = word[-length:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


from sys import stdin
from collections import defaultdict

stdin = open('tree.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    start = 0
    while True:
        start = word.find(pattern, start) + 1
        if start == 0:
            break
        else:
            counter += 1

    reminder = word[-last_index:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


def task():
    from sys import stdin
    from collections import defaultdict
    import re

    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        try:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        except ValueError:
            pattern = value[:-1]

    regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758]))
    findall = regex.findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    class AhoNode:
        def __init__(self):
            self.goto = {}
            self.out = []
            self.fail = None

    def aho_create_forest(patterns):
        root = AhoNode()

        for path in patterns:
            node = root
            for symbol in path:
                node = node.goto.setdefault(symbol, AhoNode())
            node.out.append(path)
        return root

    def aho_create_statemachine(patterns):
        root = aho_create_forest(patterns)
        queue = []
        for node in root.goto.itervalues():
            queue.append(node)
            node.fail = root

        while len(queue) > 0:
            rnode = queue.pop(0)

            for key, unode in rnode.goto.iteritems():
                queue.append(unode)
                fnode = rnode.fail
                while fnode != None and not fnode.goto.has_key(key):
                    fnode = fnode.fail
                unode.fail = fnode.goto[key] if fnode else root
                unode.out += unode.fail.out

        return root

    def aho_find_all(s, root):
        node = root
        r = 0

        for i in xrange(len(s)):
            while node != None and not node.goto.has_key(s[i]):
                node = node.fail
            if node == None:
                node = root
                continue
            node = node.goto[s[i]]
            for pattern in node.out:
                #r.append(i - len(pattern) + 1)
                r += 1
                #callback(i - len(pattern) + 1, pattern)
        return r

    root = aho_create_statemachine([pattern])
    while True:
        if not search:
            break
        reminder, index = search.pop()
        word = reminder + words[index]
        start = 0
        if len(word) > length:
            counter += aho_find_all(word, root)
            #counter += len(findall(word))

        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])

    print counter

task()


from sys import stdin

n = int(stdin.readline())

tree = {}
words = []
parents = []
for index in xrange(n-1):
    parent_string,word = stdin.readline().replace('\n','').split(' ')
    parent = int(parent_string) - 2
    words.append(list(word))
    parents.append(parent)
    if parent not in tree:
        tree[parent] = [index]
    else:
        tree[parent].append(index)

pattern = list(stdin.readline().replace('\n',''))
last_index = len(pattern) - 1

counter = 0
search = tree.pop(-1,[])
found = {-1:[]}

while True:
    if not search:
        break

    #add children edges
    index = search.pop()
    search.extend(tree.pop(index,[]))

    #parent index
    parent = parents[index]

    #get parent anchors
    anchors = found[parent]

    #cycle by edge characters
    for ch in words[index]:
        #check existing anchors
        new_anchors = []
        for anchor in anchors:
            if pattern[anchor]==ch:
                if anchor < last_index:
                    new_anchors.append(anchor+1)
                else:
                    counter += 1
        #check if this is begining of new match
        if pattern[0]==ch:
            new_anchors.append(1)
        anchors = new_anchors
    found[index] = anchors

print counter


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

first = pattern[0]
last = pattern[1:]
try:
    findall = re.compile(first +'(?=' + last + ')').findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    while True:
        if not search:
            break
        try:
            reminder, index = search.pop()
            word = reminder + words[index]
            counter += len(findall(word) or [])
            reminder = word[-length:]
            search.extend([(reminder, child) for child in tree[index]])
        except:
            break

    print counter
except:
    print -1


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

first = pattern[0]
last = pattern[1:]
findall = re.compile(first +'(?=' + last[:32758] + ')').findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    try:
        reminder, index = search.pop()
        word = reminder + words[index]
        counter += len(findall(word) or [])
        reminder = word[-length:]
        search.extend([(reminder, child) for child in tree[index]])
    except:
        break

print counter


from sys import stdin
from collections import defaultdict

#stdin = open('tree.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    start = 0
    while True:
        start = word.find(pattern, start) + 1
        if start == 0:
            break
        else:
            counter += 1

    reminder = word[-last_index:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, 0, child) for child in tree[-1]]

    shifts = KnuthMorrisPrattPattern(pattern)

    while True:
        if not search:
            break
        startPos, matchLen, index = search.pop()
        for c in words[index]:
            while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
                startPos += shifts[matchLen]
                matchLen -= shifts[matchLen]
            matchLen += 1
            if matchLen == length:
                counter += 1

        search.extend([(startPos, matchLen, child) for child in tree[index]])
        #for child in tree[index]:
        #    search.append((startPos, matchLen, child))

    print counter

task()


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

regex = re.compile('%s(?=%s)' % (pattern[0], pattern[1:]))
findall = regex.findall
length = len(pattern) - 1
counter = 0
search = [('', child) for child in tree[-1]]
extend = search.extend
pop = search.pop

while True:
    if not search:
        break
    try:
        reminder, index = pop()
        word = reminder + words[index]
        counter += len(findall(word) or [])
        reminder = word[-length:]
        extend([(reminder, child) for child in tree[index]])
    except:
        break

print counter


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

try:
    findall = re.compile('%s(?=%s)' % (pattern[0], pattern[1:])).findall
    length = len(pattern) - 1
    search = [('', child) for child in tree[-1]]

    while True:
        if not search:
            break
        try:
            reminder, index = search.pop()
            word = reminder + words[index]
            counter += len(findall(word) or [])
            reminder = word[-length:]
            search.extend([(reminder, child) for child in tree[index]])
        except:
            break

    print counter
except:
    print -1


################################
#  Test submit of nwin code 2  #
################################
from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[1:], 1):
        j = f[i]
        while s[j] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    alphas = ''.join(chr(ord('a') + c) for c in xrange(26))
    ret = [{c: 0 for c in alphas}]
    ret[0][s[0]] = 1
    for i, c in enumerate(s[1:], 1):
        d = {a: [ret[f[i]][a], i+1][c == a] for a in alphas}
        ret.append(d)
    ret.append({c: ret[f[-1]][c] for c in alphas})
    return ret
def main():
    RL = stdin.readline
    n = int(RL())
    G = defaultdict(list)
    edge_str = ['', '']
    for i in xrange(2, n+1):
        p, t = RL().split()
        G[int(p)].append(i)
        edge_str.append(t)
    ptr = RL().strip()
    ptrl = len(ptr)
    f = ff(ptr)
    stack = [(1, 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            j = f[j][c]
            if j == ptrl:
                ans = ans + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from datetime import datetime as dt
from collections import defaultdict
import gc

gc.disable()

#stdin = open('tree.txt') #<---------------------

n = int(stdin.readline())

tree = defaultdict(list)
words = []
words_append = words.append

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words_append((parent,word))
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

last_index = len(pattern) - 1
first_char = pattern[0]
second_char = pattern[1]

counter = 0
search = tree[-1]
found = defaultdict(list)

while True:
    if not search:
        break

    #add children edges
    index = search.pop()
    search.extend(tree[index])

    #word and parent index
    parent, word = words[index]

    #get parent anchors
    anchors = list(found[parent])
    remove = anchors.remove
    append = anchors.append

    candidate = False
    #cycle by edge characters
    for w in word:
        #check existing anchors
        for i,anchor in enumerate(anchors):
            if w == pattern[anchor]:
                if anchor == last_index:
                    counter += 1
                    remove(anchor)
                else:
                    anchors[i] += 1
            else:
                remove(anchor)

        if candidate:
            candidate = False
            if w == second_char:
                if last_index > 2:
                    append(2)
                else:
                    counter += 1

        #check if this is begining of new match
        if w == first_char:
            candidate = True
    if candidate:
        append(1)

    found[index] = anchors

print counter


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, 0, child) for child in tree[-1]]

    shifts = KnuthMorrisPrattPattern(pattern)
    cache = {}

    while True:
        if not search:
            break
        startPos, matchLen, index = search.pop()
        cl = 0
        key = (startPos, matchLen, words[index])
        if key in cache:
            startPos, matchLen, cl = cache[key]
        else:
            for c in words[index]:
                while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
                    startPos += shifts[matchLen]
                    matchLen -= shifts[matchLen]
                matchLen += 1
                if matchLen == length:
                    cl += 1
            cache[key] = startPos, matchLen, cl
        counter += cl
        search.extend([(startPos, matchLen, child) for child in tree[index]])

    print counter

task()


from collections import defaultdict
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    return pi

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, 0, child) for child in tree[-1]]

    #shifts = KnuthMorrisPrattPattern(pattern)
    pi = compute_prefix_function(pattern)
    cache = {}

    while True:
        if not search:
            break
        startPos, matchLen, index = search.pop()
        cl = 0
        key = (startPos, matchLen, words[index])
        if key in cache:
            startPos, matchLen, cl = cache[key]
        else:
            #for c in words[index]:
            #    while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
            #        startPos += shifts[matchLen]
            #        matchLen -= shifts[matchLen]
            #    matchLen += 1
            #    if matchLen == length:
            #        cl += 1
            for c in words[index]:
                while matchLen == length or matchLen > 0 and pattern[matchLen] != c:
                    matchLen = pi[matchLen - 1]
                if pattern[matchLen] == c:
                    matchLen += 1
                if matchLen == length:
                    cl += 1

            cache[key] = startPos, matchLen, cl
        counter += cl
        search.extend([(startPos, matchLen, child) for child in tree[index]])

    print counter

task()


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем
дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева
будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v)
и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны
строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя
вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv,
которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках
пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit
from string import ascii_lowercase

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    s = set(p)
    l = defaultdict(int)
    l.update({c: 0 for c in s})
    r = [l]
    r[0][p[0]] = 1
    for i, ch in enumerate(p[1:], start=1):
        l = defaultdict(int)
        l.update({c: [r[pi[i-1]][c], i + 1][ch == c] for c in s})
        r.append(l)
        #r.append({c: [r[pi[i-1]][c], i + 1][p == c] for c in ascii_lowercase})
    l = defaultdict(int)
    l.update({c: r[pi[-1]][c] for c in s})
    r.append(l)
    return r

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []
    pattern = ''

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            words.append(word)
            tree[int(parent_string) - 2].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, child) for child in tree[-1]]

    pi = compute_prefix_function(pattern)

    append, pop = search.append, search.pop
    while search:
        matchLen, index = pop()
        for c in words[index]:
            matchLen = pi[matchLen][c]
            if matchLen == length:
                counter += 1
        for child in tree[index]:
            append((matchLen, child))

    print counter

task()


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем
дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева
будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v)
и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны
строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя
вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv,
которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках
пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit
from string import ascii_lowercase

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    s = set(p)
    l = defaultdict(int)
    l.update({c: 0 for c in s})
    r = [l]
    r[0][p[0]] = 1
    for i, ch in enumerate(p[1:], start=1):
        l = defaultdict(int)
        l.update({c: [r[pi[i-1]][c], i + 1][ch == c] for c in s})
        r.append(l)
        #r.append({c: [r[pi[i-1]][c], i + 1][p == c] for c in ascii_lowercase})
    r.append({c: r[pi[-1]][c] for c in s})
    return r

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []
    pattern = ''

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            words.append(word)
            tree[int(parent_string) - 2].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, child) for child in tree[-1]]

    pi = compute_prefix_function(pattern)

    append, pop = search.append, search.pop
    while search:
        matchLen, index = pop()
        for c in words[index]:
            matchLen = pi[matchLen][c]
            if matchLen == length:
                counter += 1
        for child in tree[index]:
            append((matchLen, child))

    print counter

task()


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v) и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv, которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit

def KnuthMorrisPrattPattern(pattern):
    pattern = list(pattern)
    length = len(pattern)
    shifts = [1] * (length + 1)
    shift = 1
    for pos in xrange(length):
        while shift <= pos and pattern[pos] != pattern[pos - shift]:
            shift += shifts[pos - shift]
        shifts[pos + 1] = shift
    return shifts

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    return pi

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            parent = int(parent_string) - 2
            words.append(word)
            tree[parent].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, 0, child) for child in tree[-1]]

    #shifts = KnuthMorrisPrattPattern(pattern)
    pi = compute_prefix_function(pattern)
    cache = {}

    while True:
        if not search:
            break
        startPos, matchLen, index = search.pop()
        cl = 0
        key = (startPos, matchLen, words[index])
        if key in cache:
            startPos, matchLen, cl = cache[key]
        else:
            #for c in words[index]:
            #    while matchLen == length or matchLen >= 0 and pattern[matchLen] != c:
            #        startPos += shifts[matchLen]
            #        matchLen -= shifts[matchLen]
            #    matchLen += 1
            #    if matchLen == length:
            #        cl += 1
            for c in words[index]:
                while matchLen == length or matchLen > 0 and pattern[matchLen] != c:
                    matchLen = pi[matchLen - 1]
                if pattern[matchLen] == c:
                    matchLen += 1
                if matchLen == length:
                    cl += 1

            cache[key] = startPos, matchLen, cl
        counter += cl
        search.extend([(startPos, matchLen, child) for child in tree[index]])

    print counter

task()


################################
#  Test submit of nwin code 3  #
################################
from sys import stdin
from collections import defaultdict
def ff(s):
    f = [0, 0]
    for i, c in enumerate(s[1:], 1):
        j = f[i]
        while s[j] != c:
            if not j:
                f.append(0)
                break
            j = f[j]
        else:
            f.append(j+1)
    alphas = ''.join(chr(ord('a') + c) for c in xrange(26))
    ret = [{c: 0 for c in alphas}]
    ret[0][s[0]] = 1
    for i, c in enumerate(s[1:], 1):
        d = {a: [ret[f[i]][a], i+1][c == a] for a in alphas}
        ret.append(d)
    ret.append({c: ret[f[-1]][c] for c in alphas})
    return ret
def main():
    RL = stdin.readline
    n = int(RL())
    G = defaultdict(list)
    edge_str = ['', '']
    for i in xrange(2, n+1):
        p, t = RL().split()
        G[int(p)].append(i)
        edge_str.append(t)
    ptr = RL().strip()
    ptrl = len(ptr)
    f = ff(ptr)
    stack = [(1, 0)]
    ans = 0
    pp, pu = stack.pop, stack.append
    while stack:
        v, j = pp()
        for c in edge_str[v]:
            j = f[j][c]
            if j == ptrl:
                ans = ans + 1
        for x in G[v]:
            pu((x, j))
    print ans
main()


from sys import stdin
from collections import defaultdict
import re

n = int(stdin.readline())
counter = 0
tree = defaultdict(list)
words = []

for index,value in enumerate(stdin.readlines()):
    try:
        parent_string,word = value.split()
        parent = int(parent_string) - 2
        words.append(word)
        tree[parent].append(index)
    except ValueError:
        pattern = value[:-1]

findall = re.compile('%s(?=%s)' % (pattern[0], pattern[1:32758])).findall
length = len(pattern) - 1
search = [('', child) for child in tree[-1]]

while True:
    if not search:
        break
    reminder, index = search.pop()
    word = reminder + words[index]
    if len(word)>length:
        counter += len(findall(word))
    reminder = word[-length:]
    search.extend([(reminder, child) for child in tree[index]])

print counter


# coding=utf-8
"""
E. Древесно-строковая задача
ограничение по времени на тест:2 секунды
ограничение по памяти на тест:256 мегабайт
ввод:standard input
вывод:standard output

Корневым деревом называется неориентированный связный граф без циклов с выделенной вершиной, которая называется корнем
дерева. Будем считать, что вершины корневого дерева из n вершин пронумерованы от 1 до n. В этой задаче корнем дерева
будет вершина с номером 1.

Обозначим через d(v, u) длину кратчайшего по количеству ребер пути в дереве между вершинами v и u.

Родителем вершины v в корневом дереве с корнем в вершине r (v ≠ r) называется вершина pv, такая, что d(r, pv) + 1 = d(r, v)
и d(pv, v) = 1. Например, на рисунке родителем вершины v = 5 является вершина p5 = 2.

Как-то раз Поликарп раздобыл корневое дерево из n вершин. Дерево было не совсем обычное — на его ребрах были написаны
строки. Поликарп расположил дерево на плоскости так, что все ребра ведут сверху вниз при прохождении от родителя
вершины к вершине (см. рисунок). Для каждого ребра, ведущего из вершины pv в вершину v (1 < v ≤ n), известна строка sv,
которая на нем написана. Все строки записаны на ребрах сверху вниз. Например, на рисунке s7=«ba». Символы в строках
пронумерованы от 0.
"""
from collections import defaultdict
from sys import stdin, exit
from string import ascii_lowercase

def compute_prefix_function(p):
    m = len(p)
    pi = [0] * m
    k = 0
    for q in range(1, m):
        while k > 0 and p[k] != p[q]:
            k = pi[k - 1]
        if p[k] == p[q]:
            k += 1
        pi[q] = k
    r = [{c: 0 for c in ascii_lowercase}]
    r[0][p[0]] = 1
    for i, p in enumerate(p[1:], start=1):
        r.append({c: [r[pi[i-1]][c], i + 1][p == c] for c in ascii_lowercase})
    r.append({c: r[pi[-1]][c] for c in ascii_lowercase})
    return r

def task():
    n = int(stdin.readline())
    counter = 0
    tree = defaultdict(list)
    words = []
    pattern = ''

    for index, value in enumerate(stdin.readlines()):
        if index < n - 1:
            parent_string, word = value.split()
            words.append(word)
            tree[int(parent_string) - 2].append(index)
        elif index == n - 1:
            pattern = value.strip()

    length = len(pattern)
    search = [(0, child) for child in tree[-1]]

    pi = compute_prefix_function(pattern)

    append, pop = search.append, search.pop
    while search:
        matchLen, index = pop()
        for c in words[index]:
            matchLen = pi[matchLen][c]
            if matchLen == length:
                counter += 1
        for child in tree[index]:
            append((matchLen, child))

    print counter

task()