Volodya likes listening to heavy metal and (occasionally) reading. No wonder Volodya is especially interested in texts concerning his favourite music style.Volodya calls a string powerful if it starts with "heavy" and ends with "metal". Finding all powerful substrings (by substring Volodya means a subsequence of consecutive characters in a string) in a given text makes our hero especially joyful. Recently he felt an enormous fit of energy while reading a certain text. So Volodya decided to count all powerful substrings in this text and brag about it all day long. Help him in this difficult task. Two substrings are considered different if they appear at the different positions in the text.For simplicity, let us assume that Volodya's text can be represented as a single string.

Input contains a single non-empty string consisting of the lowercase Latin alphabet letters. Length of this string will not be greater than 106 characters.

Print exactly one number — the number of powerful substrings of the given string. Please, do not use the %lld specifier to read or write 64-bit integers in C++. It is preferred to use the cin, cout streams or the %I64d specifier.

NoteIn the first sample the string "heavymetalisheavymetal" contains powerful substring "heavymetal" twice, also the whole string "heavymetalisheavymetal" is certainly powerful.In the second sample the string "heavymetalismetal" contains two powerful substrings: "heavymetal" and "heavymetalismetal".

package main

import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    var s string
    fmt.Fscan(reader,&s)
    ans, heavy := int64(0), int64(0)
    for i := 0; i <= (len(s)-5); i++ {
        t := s[i:i+5]
        if t == "heavy" {
            heavy++
        } else if t == "metal" {
            ans += heavy
        }
    }
    fmt.Println(ans)
}

package main

import (
  "os"
  "bufio"
  "strings"
  "fmt"
)

func main() {
  reader := bufio.NewReader(os.Stdin)
  writer := bufio.NewWriter(os.Stdout)
  defer writer.Flush()

  line, _ := reader.ReadString('\n')
  s := strings.TrimSpace(line)
  n := len(s)
  left := make([]int64, n)
  count := int64(0)
  for i := 4; i < n; i++ {
    if s[i-4] == 'h' && s[i-3] == 'e' && s[i-2] == 'a' && s[i-1] == 'v' &&
        s[i] == 'y' {
      count++
    }
    left[i] = count
  }
  result := int64(0)
  for i := n-5; i >= 1; i-- {
    if s[i] == 'm' && s[i+1] == 'e' && s[i+2] == 't' && s[i+3] == 'a' &&
        s[i+4] == 'l' {
      count++
      result += left[i-1]
    }
  }
  writer.WriteString(fmt.Sprintf("%d\n", result))
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func getBuckets(s []byte, bkt []int, n int, end bool) {
	for i := 0; i < K; i++ {
		bkt[i] = 0
	}
	for i := 0; i < n-1; i++ {
		bkt[s[i]]++
	}
	bkt[0]++
	if end {
		bkt[0]--
		for i := 1; i < K; i++ {
			bkt[i] += bkt[i-1]
		}
	} else {
		sum := 0
		for i := 0; i < K; i++ {
			sum += bkt[i]
			bkt[i] = sum - bkt[i]
		}
	}
}

func putSuffix0(s []byte, bkt, SA []int, n, n1 int) {
	getBuckets(s, bkt, n, true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
}

func induceSAl0(s []byte, bkt, SA []int, n int, suffix bool) {
	getBuckets(s, bkt, n, false)
	bkt[0]++
	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			j := SA[i] - 1
			if j == len(s)-1 || s[j] >= s[j+1] {
				// if s[j] >= s[j+1] {
				SA[bkt[s[j]]] = j
				bkt[s[j]]++
				if !suffix && i > 0 {
					SA[i] = 0
				}
			}
		}
	}
}

func induceSAs0(s []byte, bkt, SA []int, n int, suffix bool) {
	getBuckets(s, bkt, n, true)
	for i := n - 1; i > 0; i-- {
		if SA[i] > 0 {
			j := SA[i] - 1
			// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
			if s[j] <= s[j+1] && bkt[s[j]] < i {
				SA[bkt[s[j]]] = j
				bkt[s[j]]--
				if !suffix {
					SA[i] = 0
				}
			}
		}
	}
}

func putSubstr0(s []byte, bkt, SA []int, n int) {
	getBuckets(s, bkt, n, true)
	for i := 0; i < n; i++ {
		SA[i] = 0
	}
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = false
		}
	}
	SA[0] = n - 1
}

func getLengthofLMS(s []byte, bkt, SA []int, n, pos int) int {
	if pos == n-1 {
		return 1
	}
	dist, i := 0, 1

	for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
		i++
	}

	for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
		if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
			dist = i
		}
	}
	return dist + 1
}

func nameSubstr(s []byte, bkt, SA, s1 []int, n, n1 int) int {
	for i := n1; i < n; i++ {
		SA[i] = empty
	}

	name, nameCtr := 0, 0
	prePos, preLen := 0, 0
	for i := 0; i < n1; i++ {
		diff := false
		pos := SA[i]
		length := getLengthofLMS(s, bkt, SA, n, pos)

		if length != preLen {
			diff = true
		} else {
			for d := 0; d < length; d++ {
				if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
					diff = true
					break
				}
			}
		}
		if diff {
			name = i
			nameCtr++
			SA[name] = 1
			prePos = pos
			preLen = length
		} else {
			SA[name]++
		}
		SA[n1+pos/2] = name
	}
	for i, j := n-1, n-1; i >= n1; i-- {
		if SA[i] != empty {
			SA[j] = SA[i]
			j--
		}
	}

	for succT, i := true, n1-1; i > 0; i-- {
		if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
			succT = true
			s1[i-1] += SA[s1[i-1]] - 1
		} else {
			succT = false
		}
	}
	return nameCtr
}

func getSAlms(s []byte, bkt, SA, s1 []int, n, n1 int) {
	j := n1 - 2
	s1[j+1] = n - 1
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				s1[j] = i
				j--
			}
			succT = false
		}
	}
	for i := 0; i < n1; i++ {
		SA[i] = s1[SA[i]]
	}
	for i := n1; i < n; i++ {
		SA[i] = 0

	}
}

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	putSubstr0(s, bkt, SA, n)
	induceSAl0(s, bkt, SA, n, false)
	induceSAs0(s, bkt, SA, n, false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr(s, bkt, SA, s1, n, n1)
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	getSAlms(s, bkt, SA, s1, n, n1)

	putSuffix0(s, bkt, SA, n, n1)
	induceSAl0(s, bkt, SA, n, true)
	induceSAs0(s, bkt, SA, n, true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
    "bufio"
    "bytes"
    "io"
    "os"
    "sort"
    "strconv"
)

type Scanner struct {
    r            io.Reader
    split        bufio.SplitFunc
    maxTokenSize int
    token, buf   []byte
    start, end   int
}

func NewScanner(r io.Reader) *Scanner {
    return &Scanner{
        r:            r,
        split:        bufio.ScanWords,
        maxTokenSize: 16777216,
        buf:          make([]byte, 4096),
    }
}

func (s *Scanner) advance(n int) bool {
    if n < 0 || n > s.end-s.start {
        return false
    }
    s.start += n
    return true
}

func (s *Scanner) Text() string {
    return string(s.token)
}

func (s *Scanner) Scan() bool {
    for {
        if s.end > s.start {
            advance, token, err := s.split(s.buf[s.start:s.end], false)
            if err != nil || !s.advance(advance) {
                return false
            }
            s.token = token
            if token != nil {
                return true
            }
        }
        if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
            copy(s.buf, s.buf[s.start:s.end])
            s.end -= s.start
            s.start = 0
        }
        if s.end == len(s.buf) {
            if len(s.buf) >= s.maxTokenSize {
                return false
            }
            newSize := len(s.buf) * 2
            if newSize > s.maxTokenSize {
                newSize = s.maxTokenSize
            }
            newBuf := make([]byte, newSize)
            copy(newBuf, s.buf[s.start:s.end])
            s.buf = newBuf
            s.end -= s.start
            s.start = 0
            continue
        }
        for loop := 0; ; {
            n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
            s.end += n
            if n > 0 {
                break
            }
            loop++
            if loop > 100 {
                break
            }
        }
    }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
    s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
    sc.Scan()
    n, _ := strconv.Atoi(sc.Text())
    return n
}

func ni64() int64 {
    sc.Scan()
    n, _ := strconv.ParseInt(sc.Text(), 10, 64)
    return n
}

func pri32(i ...int) {
    for _, ii := range i {
        w.WriteString(strconv.Itoa(ii))
        sp()
    }
}
func pri64(i ...int64) {
    for _, ii := range i {
        w.WriteString(strconv.FormatInt(ii, 10))
        sp()
    }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648

func getBuckets(s, bkt []int, n, K int, end bool) {
    sum := 0
    for i := 0; i < K; i++ {
        bkt[i] = 0
    }
    for i := 0; i < n; i++ {
        bkt[s[i]]++
    }
    for i := 0; i < K; i++ {
        sum += bkt[i]
        if end {
            bkt[i] = sum - 1
        } else {
            bkt[i] = sum - bkt[i]
        }
    }
}

func putSuffix0(SA, s, bkt []int, n, K, n1 int) {
    getBuckets(s, bkt, n, K, true)
    for i := n1 - 1; i > 0; i-- {
        j := SA[i]
        SA[i] = 0
        SA[bkt[s[j]]] = j
        bkt[s[j]]--
    }
    SA[0] = n - 1
}

func induceSAl0(SA, s, bkt []int, n, K int, suffix bool) {
    getBuckets(s, bkt, n, K, false)
    bkt[0]++
    for i := 0; i < n; i++ {
        if SA[i] > 0 {
            j := SA[i] - 1
            if s[j] >= s[j+1] {
                SA[bkt[s[j]]] = j
                bkt[s[j]]++
                if !suffix && i > 0 {
                    SA[i] = 0
                }
            }
        }
    }
}

func induceSAs0(SA, s, bkt []int, n, K int, suffix bool) {
    getBuckets(s, bkt, n, K, true)
    for i := n - 1; i > 0; i-- {
        if SA[i] > 0 {
            j := SA[i] - 1
            if s[j] <= s[j+1] && bkt[s[j]] < i {
                SA[bkt[s[j]]] = j
                bkt[s[j]]--
                if !suffix {
                    SA[i] = 0
                }
            }
        }
    }
}

func putSubstr0(SA, s, bkt []int, n, K int) {
    getBuckets(s, bkt, n, K, true)
    for i := 0; i < n; i++ {
        SA[i] = 0
    }
    succT, curT := 0, 0
    for i := n - 2; i > 0; i-- {
        if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
            curT = 1
        } else {
            curT = 0
        }
        if curT == 0 && succT == 1 {
            SA[bkt[s[i]]] = i
            bkt[s[i]]--
        }
        succT = curT
    }
    SA[0] = n - 1
}

func putSuffix1(SA, s []int, n1 int) {
    i, j, pos, cur, pre := 0, 0, 0, 0, -1
    for i = n1 - 1; i > 0; i-- {
        j = SA[i]
        SA[i] = empty
        cur = s[j]
        if cur != pre {
            pre = cur
            pos = cur
        }
        SA[pos] = j
        pos--
    }
}

func induceSAl1(SA, s []int, n int, suffix bool) {
    step, h := 1, 0
    for i := 0; i < n; i += step {
        step = 1
        j := SA[i] - 1
        if SA[i] <= 0 {
            continue
        }
        c, c1 := s[j], s[j+1]
        isL := c >= c1
        if !isL {
            continue
        }
        d := SA[c]
        if d >= 0 {
            foo := SA[c]
            for h = c - 1; SA[h] >= 0 || SA[h] == empty; h-- {
                bar := SA[h]
                SA[h] = foo //SA[c]
                foo = bar   //SA[c]=SA[h]
            }
            SA[h] = foo
            if h < i {
                step = 0
            }
            d = empty
        }
        if d == empty {
            if c < n-1 && SA[c+1] == empty {
                SA[c] = -1
                SA[c+1] = j
            } else {
                SA[c] = j
            }
        } else {
            pos := c - d + 1
            if pos > n-1 || SA[pos] != empty {
                for h = 0; h < (-d); h++ {
                    SA[c+h] = SA[c+h+1]
                }
                pos--
                if c < i {
                    step = 0
                }
            } else {
                SA[c]--
            }
            SA[pos] = j
        }
        c2 := 0
        isL1 := j+1 < n-1
        if isL1 {
            c2 = s[j+2]
            isL1 = c1 > c2 || (c1 == c2 && c1 < i)
        }
        if (!suffix || !isL1) && i > 0 {
            i1 := i
            if step == 0 {
                i1 = i - 1
            }
            SA[i1] = empty
        }
    }

    for i := 1; i < n; i++ {
        j := SA[i]
        if j < 0 && j != empty {
            for h = 0; h < (-j); h++ {
                SA[i+h] = SA[i+h+1]
            }
            SA[i+h] = empty
        }
    }

}

func induceSAs1(SA, s []int, n int, suffix bool) {
    step, h := 1, 0
    for i := n - 1; i > 0; i -= step {
        step = 1
        j := SA[i] - 1
        if SA[i] <= 0 {
            continue
        }
        c, c1 := s[j], s[j+1]
        isS := (c < c1) || (c == c1 && c > i)
        if !isS {
            continue
        }
        d := SA[c]
        if d >= 0 {
            foo := SA[c]
            for h = c + 1; SA[h] >= 0 || SA[h] == empty; h++ {
                bar := SA[h]
                SA[h] = foo
                foo = bar
            }
            SA[h] = foo
            if h > i {
                step = 0
            }
            d = empty
        }
        if d == empty {
            if SA[c-1] == empty {
                SA[c] = -1
                SA[c-1] = j
            } else {
                SA[c] = j
            }
        } else {
            pos := c + d - 1
            if SA[pos] != empty {
                for h = 0; h < (-d); h++ {
                    SA[c-h] = SA[c-h-1]
                }
                pos++
                if c > i {
                    step = 0
                }
            } else {
                SA[c]--
            }
            SA[pos] = j
        }
        if !suffix {
            i1 := i
            if step == 0 {
                i1 = i + 1
            }
            SA[i1] = empty
        }
    }
    if !suffix {
        for i := n - 1; i >= 0; i-- {
            j := SA[i]
            if j < 0 && j != empty {
                for h = 0; h < (-j); h++ {
                    SA[i-h] = SA[i-h-1]
                }
                SA[i-h] = empty
            }
        }
    }
}

func putSubstr1(SA, s []int, n int) {
    h := 0
    for i := 0; i < n; i++ {
        SA[i] = empty
    }
    c, c1 := 0, s[n-2]
    t, t1 := false, false
    for i := n - 2; i > 0; i-- {
        c, t = c1, t1
        c1 = s[i-1]
        t1 = c1 < c || (c1 == c && t)
        if t && !t1 {
            if SA[c] >= 0 {
                foo := SA[c]
                for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
                    bar := SA[h]
                    SA[h] = foo
                    foo = bar
                }
                SA[h] = foo
                SA[c] = empty
            }
            d := SA[c]
            if d == empty {
                if SA[c-1] == empty {
                    SA[c] = -1
                    SA[c-1] = i
                } else {
                    SA[c] = i
                }
            } else {
                pos := c + d - 1
                if SA[pos] != empty {
                    for h = 0; h < (-d); h++ {
                        SA[c-h] = SA[c-h-1]
                    }
                    pos++
                } else {
                    SA[c]--
                }
                SA[pos] = i
            }
        }
    }
    for i := n - 1; i > 0; i-- {
        j := SA[i]
        if j < 0 && j != empty {
            for h = 0; h < (-j); h++ {
                SA[i-h] = SA[i-h-1]
            }
            SA[i-h] = empty
        }
    }
    SA[0] = n - 1
}

func getLengthofLMS(s []int, n, level, x int) int {
    if x == n-1 {
        return 1
    }
    dist, i := 0, 1
    for {
        if s[x+i] < s[x+i-1] {
            break
        }
        i++
    }
    for {
        if x+i > n-1 || (s[x+i] > s[x+i-1]) {
            break
        }
        if x+i == n-1 || s[x+i] < s[x+i-1] {
            dist = i
        }
        i++
    }
    return dist + 1
}

func nameSubstr(SA, s, s1 []int, n, m, n1, level int) int {
    for i := n1; i < n; i++ {
        SA[i] = empty
    }
    name, nameCtr := 0, 0
    prePos, preLen := 0, 0
    for i := 0; i < n1; i++ {
        diff := false
        pos := SA[i]
        length := getLengthofLMS(s, n, level, pos)
        if length != preLen {
            diff = true
        } else {
            for d := 0; d < length; d++ {
                if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
                    diff = true
                    break
                }
            }
        }
        if diff {
            name = i
            nameCtr++
            SA[name] = 1
            prePos = pos
            preLen = length
        } else {
            SA[name]++
        }
        SA[n1+pos/2] = name
    }

    for i, j := n-1, m-1; i >= n1; i-- {
        if SA[i] != empty {
            SA[j] = SA[i]
            j--
        }
    }

    succT, curT := 1, 0

    for i := n1 - 1; i > 0; i-- {
        ch, ch1 := s1[i], s1[i-1]
        if ch1 < ch || (ch1 == ch && succT == 1) {
            curT = 1
        } else {
            curT = 0
        }
        if curT == 1 {
            s1[i-1] += SA[s1[i-1]] - 1
        }
        succT = curT
    }

    return nameCtr
}

func getSAlms(SA, s, s1 []int, n, n1, level int) {
    j := n1 - 2
    s1[j+1] = n - 1
    succT, curT := 0, 0
    for i := n - 2; i > 0; i-- {
        if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
            curT = 1
        } else {
            curT = 0
        }
        if curT == 0 && succT == 1 {
            s1[j] = i
            j--
        }
        succT = curT
    }
    for i := 0; i < n1; i++ {
        SA[i] = s1[SA[i]]
    }
    for i := n1; i < n; i++ {
        if level != 0 {
            SA[i] = empty
        } else {
            SA[i] = 0
        }
    }
}

func osaca(s, SA []int, n, K, m, level int) {
    var bkt []int
    if level == 0 {
        bkt = make([]int, K)
        putSubstr0(SA, s, bkt, n, K)
        induceSAl0(SA, s, bkt, n, K, false)
        induceSAs0(SA, s, bkt, n, K, false)

    } else {
        putSubstr1(SA, s, n)
        induceSAl1(SA, s, n, false)
        induceSAs1(SA, s, n, false)
    }

    n1 := 0
    for i := 0; i < n; i++ {
        if SA[i] > 0 {
            SA[n1] = SA[i]
            n1++
        }
    }
    SA1, s1 := SA, SA[m-n1:]
    nameCtr := nameSubstr(SA, s, s1, n, m, n1, level)
    if nameCtr < n1 {
        osaca(s1, SA1, n1, 0, m-n1, level+1)
    } else {
        for i := 0; i < n1; i++ {
            SA1[s1[i]] = i
        }
    }

    getSAlms(SA, s, s1, n, n1, level)

    if level == 0 {
        putSuffix0(SA, s, bkt, n, K, n1)
        induceSAl0(SA, s, bkt, n, K, true)
        induceSAs0(SA, s, bkt, n, K, true)
    } else {
        putSuffix1(SA, s, n1)
        induceSAl1(SA, s, n, true)
        induceSAs1(SA, s, n, true)

    }

}

func inducedSort2(s []byte) []int {
    a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
    for i := 0; i < len(s); i++ {
        a[i] = int(s[i])
    }
    osaca(a, sa, len(s)+1, 256, len(s)+1, 0)
    return sa[1:]
}

type Index struct {
    data []byte
    sa   []int
}

func New(data []byte) *Index {
    return &Index{data, inducedSort2(data)}
}

func (x *Index) at(i int) []byte {
    return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
    i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
    j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
    return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
    if len(s) > 0 && n != 0 {
        matches := x.lookupAll(s)
        if n < 0 || len(matches) < n {
            n = len(matches)
        }
        if n > 0 {
            result = make([]int, n)
            copy(result, matches)
        }
    }
    return
}

func main() {
    index := New([]byte(next()))
    heavy := index.Lookup([]byte("heavy"), -1)
    metal := index.Lookup([]byte("metal"), -1)
    sort.Ints(heavy)
    sort.Ints(metal)
    ans := int64(0)
    cur := 0
    for i := 0; i < len(heavy); i++ {
        for cur < len(metal) && metal[cur] <= heavy[i]+4 {
            cur++
        }
        ans += int64(len(metal) - cur)
    }
    pri64(ans)
    w.Flush()
}

//

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	getBuckets := func(end bool) {
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n-1; i++ {
			bkt[s[i]]++
		}
		bkt[0]++
		if end {
			bkt[0]--
			for i := 1; i < K; i++ {
				bkt[i] += bkt[i-1]
			}
		} else {
			sum := 0
			for i := 0; i < K; i++ {
				sum += bkt[i]
				bkt[i] = sum - bkt[i]
			}
		}
	}

	putSuffix0 := func() {
		getBuckets(true)
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = 0
			SA[bkt[s[j]]] = j
			bkt[s[j]]--
		}
		SA[0] = n - 1
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					SA[bkt[s[i]]] = i
					bkt[s[i]]--
				}
				succT = false
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}

		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)

			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}
		for i, j := n-1, n-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}
		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = 0

		}
	}

	putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix0()
	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

//

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	getBuckets := func(end bool) {
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n-1; i++ {
			bkt[s[i]]++
		}
		bkt[0]++
		if end {
			bkt[0]--
			for i := 1; i < K; i++ {
				bkt[i] += bkt[i-1]
			}
		} else {
			sum := 0
			for i := 0; i < K; i++ {
				sum += bkt[i]
				bkt[i] = sum - bkt[i]
			}
		}
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}

		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)

			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}
		for i, j := n-1, n-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}
		return nameCtr
	}

	// putSubstr0 := func() {
	getBuckets(true)
	for i := 0; i < n; i++ {
		SA[i] = 0
	}
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = false
		}
	}
	SA[0] = n - 1
	// }
	// putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	// getSAlms := func() {
	j := n1 - 2
	s1[j+1] = n - 1
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				s1[j] = i
				j--
			}
			succT = false
		}
	}
	for i := 0; i < n1; i++ {
		SA[i] = s1[SA[i]]
	}
	for i := n1; i < n; i++ {
		SA[i] = 0

	}
	// }
	// getSAlms()

	// putSuffix0 := func() {
	getBuckets(true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
	// }
	// putSuffix0()

	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
  "bufio"
  "bytes"
  "io"
  "os"
  "sort"
  "strconv"
)

type Scanner struct {
  r            io.Reader
  split        bufio.SplitFunc
  maxTokenSize int
  token, buf   []byte
  start, end   int
}

func NewScanner(r io.Reader) *Scanner {
  return &Scanner{
    r:            r,
    split:        bufio.ScanWords,
    maxTokenSize: 16777216,
    buf:          make([]byte, 4096),
  }
}

func (s *Scanner) advance(n int) bool {
  if n < 0 || n > s.end-s.start {
    return false
  }
  s.start += n
  return true
}

func (s *Scanner) Text() string {
  return string(s.token)
}

func (s *Scanner) Scan() bool {
  for {
    if s.end > s.start {
      advance, token, err := s.split(s.buf[s.start:s.end], false)
      if err != nil || !s.advance(advance) {
        return false
      }
      s.token = token
      if token != nil {
        return true
      }
    }
    if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
      copy(s.buf, s.buf[s.start:s.end])
      s.end -= s.start
      s.start = 0
    }
    if s.end == len(s.buf) {
      if len(s.buf) >= s.maxTokenSize {
        return false
      }
      newSize := len(s.buf) * 2
      if newSize > s.maxTokenSize {
        newSize = s.maxTokenSize
      }
      newBuf := make([]byte, newSize)
      copy(newBuf, s.buf[s.start:s.end])
      s.buf = newBuf
      s.end -= s.start
      s.start = 0
      continue
    }
    for loop := 0; ; {
      n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
      s.end += n
      if n > 0 {
        break
      }
      loop++
      if loop > 100 {
        break
      }
    }
  }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
  s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
  sc.Scan()
  n, _ := strconv.Atoi(sc.Text())
  return n
}

func ni64() int64 {
  sc.Scan()
  n, _ := strconv.ParseInt(sc.Text(), 10, 64)
  return n
}

func pri32(i ...int) {
  for _, ii := range i {
    w.WriteString(strconv.Itoa(ii))
    sp()
  }
}
func pri64(i ...int64) {
  for _, ii := range i {
    w.WriteString(strconv.FormatInt(ii, 10))
    sp()
  }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }
func getBuckets(s, bkt []int, end bool) {
  alphaSize := len(bkt)
  for i := 0; i < alphaSize; i++ {
    bkt[i] = 0
  }
  for i := 0; i < len(s); i++ {
    bkt[s[i]]++
  }
  sum := 0
  for i := 0; i < alphaSize; i++ {
    sum += bkt[i]
    if end {
      bkt[i] = sum
    } else {
      bkt[i] = sum - bkt[i]
    }
  }
}

func induceSAl(s, bkt []int, t []bool, SA []int, end bool) {
  getBuckets(s, bkt, end)
  for i := 0; i < len(s); i++ {
    j := SA[i] - 1
    if j >= 0 && t[j] == false {
      SA[bkt[s[j]]] = j
      bkt[s[j]]++
    }
  }
}

func induceSAs(s, bkt []int, t []bool, SA []int, end bool) {
  getBuckets(s, bkt, end)
  for i := len(s) - 1; i >= 0; i-- {
    j := SA[i] - 1
    if j >= 0 && t[j] == true {
      bkt[s[j]]--
      SA[bkt[s[j]]] = j
    }
  }
}

func isLMS(t []bool, pos int) bool {
  return pos > 0 && t[pos] == true && t[pos-1] == false
}

func sais(s, SA []int, alphaSize int) {
  n := len(s)
  t := make([]bool, n)
  t[n-1] = true
  t[n-2] = false
  for i := n - 3; i >= 0; i-- {
    if s[i] < s[i+1] || (s[i] == s[i+1] && t[i+1] == true) {
      t[i] = true
    } else {
      t[i] = false
    }
  }
  bkt := make([]int, alphaSize+1)
  getBuckets(s, bkt, true)
  for i := 0; i < n; i++ {
    SA[i] = -1
  }
  for i := 1; i < n; i++ {
    if isLMS(t, i) {

      bkt[s[i]]--
      SA[bkt[s[i]]] = i
    }
  }

  induceSAl(s, bkt, t, SA, false)
  induceSAs(s, bkt, t, SA, true)

  n1 := 0
  for i := 0; i < n; i++ {
    if isLMS(t, SA[i]) {
      SA[n1] = SA[i]
      n1++
    }
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }

  name, prev := 0, -1
  for i := 0; i < n1; i++ {
    pos := SA[i]
    diff := false
    for d := 0; d < n; d++ {
      if prev == -1 || s[pos+d] != s[prev+d] || t[pos+d] != t[prev+d] {
        diff = true
        break
      } else if d > 0 && (isLMS(t, pos+d) || isLMS(t, prev+d)) {
        break
      }
    }
    if diff {
      name++
      prev = pos
    }
    if pos%2 == 0 {
      pos = pos / 2
    } else {
      pos = (pos - 1) / 2
    }
    SA[n1+pos] = name - 1
  }
  for i, j := n-1, n-1; i >= n1; i-- {
    if SA[i] >= 0 {
      SA[j] = SA[i]
      j--
    }
  }

  SA1, s1 := SA[:n1], SA[n-n1:]

  if name < n1 {

    sais(s1, SA1, name-1)
  } else {
    for i := 0; i < n1; i++ {
      SA1[s1[i]] = i
    }
  }
  getBuckets(s, bkt, true)
  for i, j := 1, 0; i < n; i++ {
    if isLMS(t, i) {
      s1[j] = i
      j++
    }
  }
  for i := 0; i < n1; i++ {
    SA1[i] = s1[SA1[i]]
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }
  for i := n1 - 1; i >= 0; i-- {
    j := SA[i]
    SA[i] = -1
    bkt[s[j]]--
    SA[bkt[s[j]]] = j
  }
  induceSAl(s, bkt, t, SA, false)
  induceSAs(s, bkt, t, SA, true)
}

func SAIS(s []byte) []int {
  a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
  for i := 0; i < len(s); i++ {
    a[i] = int(s[i])
  }
  sais(a, sa, 256)
  return sa[1:]
}

type Index struct {
  data []byte
  sa   []int
}

func New(data []byte) *Index {
  return &Index{data, SAIS(data)}
}

func (x *Index) at(i int) []byte {
  return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
  i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
  j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
  return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
  if len(s) > 0 && n != 0 {
    matches := x.lookupAll(s)
    if n < 0 || len(matches) < n {
      n = len(matches)
    }
    if n > 0 {
      result = make([]int, n)
      copy(result, matches)
    }
  }
  return
}

func main() {
  index := New([]byte(next()))
  heavy := index.Lookup([]byte("heavy"), -1)
  metal := index.Lookup([]byte("metal"), -1)
  sort.Ints(heavy)
  sort.Ints(metal)
  ans := int64(0)
  cur := 0
  for i := 0; i < len(heavy); i++ {
    for cur < len(metal) && metal[cur] <= heavy[i]+4 {
      cur++
    }
    ans += int64(len(metal) - cur)
  }
  pri64(ans)
  w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func getBuckets(s []byte, bkt []int, n int, end bool) {
	for i := 0; i < K; i++ {
		bkt[i] = 0
	}
	for i := 0; i < n-1; i++ {
		bkt[s[i]]++
	}
	bkt[0]++
	if end {
		bkt[0]--
		for i := 1; i < K; i++ {
			bkt[i] += bkt[i-1]
		}
	} else {
		sum := 0
		for i := 0; i < K; i++ {
			sum += bkt[i]
			bkt[i] = sum - bkt[i]
		}
	}
}

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	putSuffix0 := func() {
		getBuckets(s, bkt, n, true)
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = 0
			SA[bkt[s[j]]] = j
			bkt[s[j]]--
		}
		SA[0] = n - 1
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(s, bkt, n, false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(s, bkt, n, true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(s, bkt, n, true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					SA[bkt[s[i]]] = i
					bkt[s[i]]--
				}
				succT = false
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}

		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)

			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}
		for i, j := n-1, n-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}
		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = 0

		}
	}

	putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix0()
	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
  "bufio"
  "bytes"
  "io"
  "os"
  "sort"
  "strconv"
)

type Scanner struct {
  r            io.Reader
  split        bufio.SplitFunc
  maxTokenSize int
  token, buf   []byte
  start, end   int
}

func NewScanner(r io.Reader) *Scanner {
  return &Scanner{
    r:            r,
    split:        bufio.ScanWords,
    maxTokenSize: 16777216,
    buf:          make([]byte, 4096),
  }
}

func (s *Scanner) advance(n int) bool {
  if n < 0 || n > s.end-s.start {
    return false
  }
  s.start += n
  return true
}

func (s *Scanner) Text() string {
  return string(s.token)
}

func (s *Scanner) Scan() bool {
  for {
    if s.end > s.start {
      advance, token, err := s.split(s.buf[s.start:s.end], false)
      if err != nil || !s.advance(advance) {
        return false
      }
      s.token = token
      if token != nil {
        return true
      }
    }
    if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
      copy(s.buf, s.buf[s.start:s.end])
      s.end -= s.start
      s.start = 0
    }
    if s.end == len(s.buf) {
      if len(s.buf) >= s.maxTokenSize {
        return false
      }
      newSize := len(s.buf) * 2
      if newSize > s.maxTokenSize {
        newSize = s.maxTokenSize
      }
      newBuf := make([]byte, newSize)
      copy(newBuf, s.buf[s.start:s.end])
      s.buf = newBuf
      s.end -= s.start
      s.start = 0
      continue
    }
    for loop := 0; ; {
      n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
      s.end += n
      if n > 0 {
        break
      }
      loop++
      if loop > 100 {
        break
      }
    }
  }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
  s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
  sc.Scan()
  n, _ := strconv.Atoi(sc.Text())
  return n
}

func ni64() int64 {
  sc.Scan()
  n, _ := strconv.ParseInt(sc.Text(), 10, 64)
  return n
}

func pri32(i ...int) {
  for _, ii := range i {
    w.WriteString(strconv.Itoa(ii))
    sp()
  }
}
func pri64(i ...int64) {
  for _, ii := range i {
    w.WriteString(strconv.FormatInt(ii, 10))
    sp()
  }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

func getBuckets(s []int, alphaSize int, end bool) []int {
  bkt := make([]int, alphaSize)
  for i := 0; i < len(s); i++ {
    bkt[s[i]]++
  }
  sum := 0
  for i := 0; i < alphaSize; i++ {
    sum += bkt[i]
    if end {
      bkt[i] = sum
    } else {
      bkt[i] = sum - bkt[i]
    }
  }
  return bkt
}

func induceSAl(s, t, SA []int, alphaSize int, end bool) {
  bkt := getBuckets(s, alphaSize+1, end)
  for i := 0; i < len(s); i++ {
    j := SA[i] - 1
    if j >= 0 && t[j] == 0 {
      SA[bkt[s[j]]] = j
      bkt[s[j]]++
    }
  }
}

func induceSAs(s, t, SA []int, alphaSize int, end bool) {
  bkt := getBuckets(s, alphaSize+1, end)
  for i := len(s) - 1; i >= 0; i-- {
    j := SA[i] - 1
    if j >= 0 && t[j] == 1 {
      bkt[s[j]]--
      SA[bkt[s[j]]] = j
    }
  }
}

func isLMS(t []int, pos int) bool {
  return pos > 0 && t[pos] == 1 && t[pos-1] == 0
}

func sais(s, SA []int, alphaSize int) {
  n := len(s)
  t := make([]int, n)
  t[n-1] = 1
  t[n-2] = 0
  for i := n - 3; i >= 0; i-- {
    if s[i] < s[i+1] || (s[i] == s[i+1] && t[i+1] == 1) {
      t[i] = 1
    } else {
      t[i] = 0
    }
  }
  bkt := getBuckets(s, alphaSize+1, true)
  for i := 0; i < n; i++ {
    SA[i] = -1
  }
  for i := 1; i < n; i++ {
    if isLMS(t, i) {

      bkt[s[i]]--
      SA[bkt[s[i]]] = i
    }
  }

  induceSAl(s, t, SA, alphaSize, false)

  induceSAs(s, t, SA, alphaSize, true)

  n1 := 0
  for i := 0; i < n; i++ {
    if isLMS(t, SA[i]) {
      SA[n1] = SA[i]
      n1++
    }
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }

  name, prev := 0, -1
  for i := 0; i < n1; i++ {
    pos := SA[i]
    diff := false
    for d := 0; d < n; d++ {
      if prev == -1 || s[pos+d] != s[prev+d] || t[pos+d] != t[prev+d] {
        diff = true
        break
      } else if d > 0 && (isLMS(t, pos+d) || isLMS(t, prev+d)) {
        break
      }
    }
    if diff {
      name++
      prev = pos
    }
    if pos%2 == 0 {
      pos = pos / 2
    } else {
      pos = (pos - 1) / 2
    }
    SA[n1+pos] = name - 1
  }
  for i, j := n-1, n-1; i >= n1; i-- {
    if SA[i] >= 0 {
      SA[j] = SA[i]
      j--
    }
  }

  SA1, s1 := SA[:n1], SA[n-n1:]

  if name < n1 {

    sais(s1, SA1, name-1)
  } else {
    for i := 0; i < n1; i++ {
      SA1[s1[i]] = i
    }
  }
  bkt = getBuckets(s, alphaSize+1, true)
  for i, j := 1, 0; i < n; i++ {
    if isLMS(t, i) {
      s1[j] = i
      j++
    }
  }
  for i := 0; i < n1; i++ {
    SA1[i] = s1[SA1[i]]
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }
  for i := n1 - 1; i >= 0; i-- {
    j := SA[i]
    SA[i] = -1
    bkt[s[j]]--
    SA[bkt[s[j]]] = j
  }
  induceSAl(s, t, SA, alphaSize, false)
  induceSAs(s, t, SA, alphaSize, true)
}

func SAIS(s []byte) []int {
  a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
  for i := 0; i < len(s); i++ {
    a[i] = int(s[i])
  }
  sais(a, sa, 256)
  return sa[1:]
}

type Index struct {
  data []byte
  sa   []int
}

func New(data []byte) *Index {
  return &Index{data, SAIS(data)}
}

func (x *Index) at(i int) []byte {
  return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
  i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
  j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
  return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
  if len(s) > 0 && n != 0 {
    matches := x.lookupAll(s)
    if n < 0 || len(matches) < n {
      n = len(matches)
    }
    if n > 0 {
      result = make([]int, n)
      copy(result, matches)
    }
  }
  return
}

func main() {
  index := New([]byte(next()))
  heavy := index.Lookup([]byte("heavy"), -1)
  metal := index.Lookup([]byte("metal"), -1)
  sort.Ints(heavy)
  sort.Ints(metal)
  ans := int64(0)
  cur := 0
  for i := 0; i < len(heavy); i++ {
    for cur < len(metal) && metal[cur] <= heavy[i]+4 {
      cur++
    }
    ans += int64(len(metal) - cur)
  }
  pri64(ans)
  w.Flush()
}

package main

import (
  "bufio"
  "index/suffixarray"
  "io"
  "os"
  "sort"
  "strconv"
)

type Scanner struct {
  r            io.Reader
  split        bufio.SplitFunc
  maxTokenSize int
  token, buf   []byte
  start, end   int
}

func NewScanner(r io.Reader) *Scanner {
  return &Scanner{
    r:            r,
    split:        bufio.ScanWords,
    maxTokenSize: 16777216,
    buf:          make([]byte, 4096),
  }
}

func (s *Scanner) advance(n int) bool {
  if n < 0 || n > s.end-s.start {
    return false
  }
  s.start += n
  return true
}

func (s *Scanner) Text() string {
  return string(s.token)
}

func (s *Scanner) Scan() bool {
  for {
    if s.end > s.start {
      advance, token, err := s.split(s.buf[s.start:s.end], false)
      if err != nil || !s.advance(advance) {
        return false
      }
      s.token = token
      if token != nil {
        return true
      }
    }
    if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
      copy(s.buf, s.buf[s.start:s.end])
      s.end -= s.start
      s.start = 0
    }
    if s.end == len(s.buf) {
      if len(s.buf) >= s.maxTokenSize {
        return false
      }
      newSize := len(s.buf) * 2
      if newSize > s.maxTokenSize {
        newSize = s.maxTokenSize
      }
      newBuf := make([]byte, newSize)
      copy(newBuf, s.buf[s.start:s.end])
      s.buf = newBuf
      s.end -= s.start
      s.start = 0
      continue
    }
    for loop := 0; ; {
      n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
      s.end += n
      if n > 0 {
        break
      }
      loop++
      if loop > 100 {
        break
      }
    }
  }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
  s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
  sc.Scan()
  n, _ := strconv.Atoi(sc.Text())
  return n
}

func ni64() int64 {
  sc.Scan()
  n, _ := strconv.ParseInt(sc.Text(), 10, 64)
  return n
}

func pri32(i ...int) {
  for _, ii := range i {
    w.WriteString(strconv.Itoa(ii))
    sp()
  }
}
func pri64(i ...int64) {
  for _, ii := range i {
    w.WriteString(strconv.FormatInt(ii, 10))
    sp()
  }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

func main() {
  index := suffixarray.New([]byte(next()))
  heavy := index.Lookup([]byte("heavy"), -1)
  metal := index.Lookup([]byte("metal"), -1)
  sort.Ints(heavy)
  sort.Ints(metal)
  ans := int64(0)
  cur := 0
  for i := 0; i < len(heavy); i++ {
    for cur < len(metal) && metal[cur] <= heavy[i]+4 {
      cur++
    }
    ans += int64(len(metal) - cur)
  }
  pri64(ans)
  w.Flush()
}
//2

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	getBuckets := func(end bool) {
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n-1; i++ {
			bkt[s[i]]++
		}
		bkt[0]++
		if end {
			bkt[0]--
			for i := 1; i < K; i++ {
				bkt[i] += bkt[i-1]
			}
		} else {
			sum := 0
			for i := 0; i < K; i++ {
				sum += bkt[i]
				bkt[i] = sum - bkt[i]
			}
		}
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					SA[bkt[s[i]]] = i
					bkt[s[i]]--
				}
				succT = false
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}

		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)

			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}
		for i, j := n-1, n-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}
		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = 0

		}
	}

	putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	getSAlms()

	// putSuffix0 := func() {
	getBuckets(true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
	// }
	// putSuffix0()
	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648

func osaca(s, SA []int, n, K, m, level int) {
	var bkt []int

	var SA1, s1 []int

	var n1 int

	getBuckets := func(end bool) {
		sum := 0
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n; i++ {
			bkt[s[i]]++
		}
		for i := 0; i < K; i++ {
			sum += bkt[i]
			if end {
				bkt[i] = sum - 1
			} else {
				bkt[i] = sum - bkt[i]
			}
		}
	}

	putSuffix0 := func() {
		getBuckets(true)
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = 0
			SA[bkt[s[j]]] = j
			bkt[s[j]]--
		}
		SA[0] = n - 1
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = curT
		}
		SA[0] = n - 1
	}

	putSuffix1 := func() {
		i, j, pos, cur, pre := 0, 0, 0, 0, -1
		for i = n1 - 1; i > 0; i-- {
			j = SA[i]
			SA[i] = empty
			cur = s[j]
			if cur != pre {
				pre = cur
				pos = cur
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step, h := 1, 0
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s[j], s[j+1]
			isL := c >= c1
			if !isL {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c - 1; SA[h] >= 0 || SA[h] == empty; h-- {
					bar := SA[h]
					SA[h] = foo //SA[c]
					foo = bar   //SA[c]=SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if c < n-1 && SA[c+1] == empty {
					SA[c] = -1
					SA[c+1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c+h] = SA[c+h+1]
					}
					pos--
					if c < i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			c2 := 0
			isL1 := j+1 < n-1
			if isL1 {
				c2 = s[j+2]
				isL1 = c1 > c2 || (c1 == c2 && c1 < i)
			}
			if (!suffix || !isL1) && i > 0 {
				i1 := i
				if step == 0 {
					i1 = i - 1
				}
				SA[i1] = empty
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i+h] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step, h := 1, 0
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s[j], s[j+1]
			isS := (c < c1) || (c == c1 && c > i)
			if !isS {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c + 1; SA[h] >= 0 || SA[h] == empty; h++ {
					bar := SA[h]
					SA[h] = foo
					foo = bar
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[c-1] == empty {
					SA[c] = -1
					SA[c-1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c + d - 1
				if SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c-h] = SA[c-h-1]
					}
					pos++
					if c > i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			if !suffix {
				i1 := i
				if step == 0 {
					i1 = i + 1
				}
				SA[i1] = empty
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h = 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i-h] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					foo := SA[c]
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						bar := SA[h]
						SA[h] = foo
						foo = bar
					}
					SA[h] = foo
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(level, x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && (s[x+i] <= s[x+i-1]); i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func(m, level int) int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(level, pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		succT, curT := 1, 0

		for i := n1 - 1; i > 0; i-- {
			ch, ch1 := s1[i], s1[i-1]
			if ch1 < ch || (ch1 == ch && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 1 {
				s1[i-1] += SA[s1[i-1]] - 1
			}
			succT = curT
		}

		return nameCtr
	}

	getSAlms := func(level int) {
		j := n1 - 2
		s1[j+1] = n - 1
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				s1[j] = i
				j--
			}
			succT = curT
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			if level != 0 {
				SA[i] = empty
			} else {
				SA[i] = 0
			}
		}
	}

	if level == 0 {
		bkt = make([]int, K)
		putSubstr0()
		induceSAl0(false)
		induceSAs0(false)

	} else {
		putSubstr1()
		induceSAl1(false)
		induceSAs1(false)
	}

	n1 = 0
	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]
	nameCtr := nameSubstr(m, level)
	if nameCtr < n1 {
		osaca(s1, SA1, n1, 0, m-n1, level+1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms(level)

	if level == 0 {
		putSuffix0()
		induceSAl0(true)
		induceSAs0(true)
	} else {
		putSuffix1()
		induceSAl1(true)
		induceSAs1(true)

	}

}

func inducedSort2(s []byte) []int {
	a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
	for i := 0; i < len(s); i++ {
		a[i] = int(s[i])
	}
	osaca(a, sa, len(s)+1, 256, len(s)+1, 0)
	return sa[1:]
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, inducedSort2(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

//

package main

import (
  "bufio"
  "bytes"
  "io"
  "os"
  "sort"
  "strconv"
)

type Scanner struct {
  r            io.Reader
  split        bufio.SplitFunc
  maxTokenSize int
  token, buf   []byte
  start, end   int
}

func NewScanner(r io.Reader) *Scanner {
  return &Scanner{
    r:            r,
    split:        bufio.ScanWords,
    maxTokenSize: 16777216,
    buf:          make([]byte, 4096),
  }
}

func (s *Scanner) advance(n int) bool {
  if n < 0 || n > s.end-s.start {
    return false
  }
  s.start += n
  return true
}

func (s *Scanner) Text() string {
  return string(s.token)
}

func (s *Scanner) Scan() bool {
  for {
    if s.end > s.start {
      advance, token, err := s.split(s.buf[s.start:s.end], false)
      if err != nil || !s.advance(advance) {
        return false
      }
      s.token = token
      if token != nil {
        return true
      }
    }
    if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
      copy(s.buf, s.buf[s.start:s.end])
      s.end -= s.start
      s.start = 0
    }
    if s.end == len(s.buf) {
      if len(s.buf) >= s.maxTokenSize {
        return false
      }
      newSize := len(s.buf) * 2
      if newSize > s.maxTokenSize {
        newSize = s.maxTokenSize
      }
      newBuf := make([]byte, newSize)
      copy(newBuf, s.buf[s.start:s.end])
      s.buf = newBuf
      s.end -= s.start
      s.start = 0
      continue
    }
    for loop := 0; ; {
      n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
      s.end += n
      if n > 0 {
        break
      }
      loop++
      if loop > 100 {
        break
      }
    }
  }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
  s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
  sc.Scan()
  n, _ := strconv.Atoi(sc.Text())
  return n
}

func ni64() int64 {
  sc.Scan()
  n, _ := strconv.ParseInt(sc.Text(), 10, 64)
  return n
}

func pri32(i ...int) {
  for _, ii := range i {
    w.WriteString(strconv.Itoa(ii))
    sp()
  }
}
func pri64(i ...int64) {
  for _, ii := range i {
    w.WriteString(strconv.FormatInt(ii, 10))
    sp()
  }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

// func getBuckets(s, bkt []int, end bool) {
//   alphaSize := len(bkt)
//   for i := 0; i < alphaSize; i++ {
//     bkt[i] = 0
//   }
//   for i := 0; i < len(s); i++ {
//     bkt[s[i]]++
//   }
//   sum := 0
//   for i := 0; i < alphaSize; i++ {
//     sum += bkt[i]
//     if end {
//       bkt[i] = sum
//     } else {
//       bkt[i] = sum - bkt[i]
//     }
//   }
// }

// func induceSAl(s, bkt []int, t []bool, SA []int, end bool) {
//   getBuckets(s, bkt, end)
//   for i := 0; i < len(s); i++ {
//     j := SA[i] - 1
//     if j >= 0 && t[j] == false {
//       SA[bkt[s[j]]] = j
//       bkt[s[j]]++
//     }
//   }
// }

// func induceSAs(s, bkt []int, t []bool, SA []int, end bool) {
//   getBuckets(s, bkt, end)
//   for i := len(s) - 1; i >= 0; i-- {
//     j := SA[i] - 1
//     if j >= 0 && t[j] == true {
//       bkt[s[j]]--
//       SA[bkt[s[j]]] = j
//     }
//   }
// }

// func isLMS(t []bool, pos int) bool {
//   return pos > 0 && t[pos] == true && t[pos-1] == false
// }

func sais(s, SA []int, alphaSize int) {
  n := len(s)
  t := make([]bool, n)
  t[n-1] = true
  for i := n - 3; i >= 0; i-- {
    if s[i] < s[i+1] || (s[i] == s[i+1] && t[i+1]) {
      t[i] = true
    }
  }
  bkt := make([]int, alphaSize+1)

  isLMS := func(pos int) bool {
    return pos > 0 && t[pos] && !t[pos-1]
  }

  getBuckets := func(end bool) {
    for i := 0; i < alphaSize+1; i++ {
      bkt[i] = 0
    }
    for i := 0; i < len(s); i++ {
      bkt[s[i]]++
    }
    sum := 0
    for i := 0; i < alphaSize+1; i++ {
      sum += bkt[i]
      if end {
        bkt[i] = sum
      } else {
        bkt[i] = sum - bkt[i]
      }
    }
  }

  induceLS := func() {
    getBuckets(false)
    for i := 0; i < n; i++ {
      j := SA[i] - 1
      if j >= 0 && !t[j] {
        SA[bkt[s[j]]] = j
        bkt[s[j]]++
      }
    }
    getBuckets(true)
    for i := n - 1; i >= 0; i-- {
      j := SA[i] - 1
      if j >= 0 && t[j] {
        bkt[s[j]]--
        SA[bkt[s[j]]] = j
      }
    }
  }

  // getBuckets(s, bkt, true)
  getBuckets(true)
  for i := 0; i < n; i++ {
    SA[i] = -1
  }
  for i := 1; i < n; i++ {
    // if isLMS(t, i) {
    if isLMS(i) {
      bkt[s[i]]--
      SA[bkt[s[i]]] = i
    }
  }

  // induceSAl(s, bkt, t, SA, false)
  // induceSAs(s, bkt, t, SA, true)
  induceLS()

  n1 := 0
  for i := 0; i < n; i++ {
    // if isLMS(t, SA[i]) {
    if isLMS(SA[i]) {
      SA[n1] = SA[i]
      n1++
    }
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }

  name, prev := 0, -1
  for i := 0; i < n1; i++ {
    pos := SA[i]
    diff := false
    for d := 0; d < n; d++ {
      if prev == -1 || s[pos+d] != s[prev+d] || t[pos+d] != t[prev+d] {
        diff = true
        break
        // } else if d > 0 && (isLMS(t, pos+d) || isLMS(t, prev+d)) {
      } else if d > 0 && (isLMS(pos+d) || isLMS(prev+d)) {
        break
      }
    }
    if diff {
      name++
      prev = pos
    }
    if pos%2 == 0 {
      pos = pos / 2
    } else {
      pos = (pos - 1) / 2
    }
    SA[n1+pos] = name - 1
  }
  for i, j := n-1, n-1; i >= n1; i-- {
    if SA[i] >= 0 {
      SA[j] = SA[i]
      j--
    }
  }

  SA1, s1 := SA[:n1], SA[n-n1:]

  if name < n1 {
    sais(s1, SA1, name-1)
  } else {
    for i := 0; i < n1; i++ {
      SA1[s1[i]] = i
    }
  }
  // getBuckets(s, bkt, true)
  getBuckets(true)
  for i, j := 1, 0; i < n; i++ {
    // if isLMS(t, i) {
    if isLMS(i) {
      s1[j] = i
      j++
    }
  }
  for i := 0; i < n1; i++ {
    SA1[i] = s1[SA1[i]]
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }
  for i := n1 - 1; i >= 0; i-- {
    j := SA[i]
    SA[i] = -1
    bkt[s[j]]--
    SA[bkt[s[j]]] = j
  }
  // induceSAl(s, bkt, t, SA, false)
  // induceSAs(s, bkt, t, SA, true)
  induceLS()
}

func SAIS(s []byte) []int {
  a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
  for i := 0; i < len(s); i++ {
    a[i] = int(s[i])
  }
  sais(a, sa, 256)
  return sa[1:]
}

type Index struct {
  data []byte
  sa   []int
}

func New(data []byte) *Index {
  return &Index{data, SAIS(data)}
}

func (x *Index) at(i int) []byte {
  return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
  i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
  j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
  return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
  if len(s) > 0 && n != 0 {
    matches := x.lookupAll(s)
    if n < 0 || len(matches) < n {
      n = len(matches)
    }
    if n > 0 {
      result = make([]int, n)
      copy(result, matches)
    }
  }
  return
}

func main() {
  index := New([]byte(next()))
  heavy := index.Lookup([]byte("heavy"), -1)
  metal := index.Lookup([]byte("metal"), -1)
  sort.Ints(heavy)
  sort.Ints(metal)
  ans := int64(0)
  cur := 0
  for i := 0; i < len(heavy); i++ {
    for cur < len(metal) && metal[cur] <= heavy[i]+4 {
      cur++
    }
    ans += int64(len(metal) - cur)
  }
  pri64(ans)
  w.Flush()
}
//

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	getBuckets := func(end bool) {
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n-1; i++ {
			bkt[s[i]]++
		}
		bkt[0]++
		if end {
			bkt[0]--
			for i := 1; i < K; i++ {
				bkt[i] += bkt[i-1]
			}
		} else {
			sum := 0
			for i := 0; i < K; i++ {
				sum += bkt[i]
				bkt[i] = sum - bkt[i]
			}
		}
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	// putSubstr0 := func() {
	getBuckets(true)
	for i := 0; i < n; i++ {
		SA[i] = 0
	}
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = false
		}
	}
	SA[0] = n - 1
	// }
	// putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	// nameSubstr := func() int {
	for i := n1; i < n; i++ {
		SA[i] = empty
	}

	name, nameCtr := 0, 0
	prePos, preLen := 0, 0
	for i := 0; i < n1; i++ {
		diff := false
		pos := SA[i]
		length := getLengthofLMS(pos)

		if length != preLen {
			diff = true
		} else {
			for d := 0; d < length; d++ {
				if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
					diff = true
					break
				}
			}
		}
		if diff {
			name = i
			nameCtr++
			SA[name] = 1
			prePos = pos
			preLen = length
		} else {
			SA[name]++
		}
		SA[n1+pos/2] = name
	}
	for i, j := n-1, n-1; i >= n1; i-- {
		if SA[i] != empty {
			SA[j] = SA[i]
			j--
		}
	}

	for succT, i := true, n1-1; i > 0; i-- {
		if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
			succT = true
			s1[i-1] += SA[s1[i-1]] - 1
		} else {
			succT = false
		}
	}
	// 	return nameCtr
	// }
	// nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	// getSAlms := func() {
	j := n1 - 2
	s1[j+1] = n - 1
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				s1[j] = i
				j--
			}
			succT = false
		}
	}
	for i := 0; i < n1; i++ {
		SA[i] = s1[SA[i]]
	}
	for i := n1; i < n; i++ {
		SA[i] = 0

	}
	// }
	// getSAlms()

	// putSuffix0 := func() {
	getBuckets(true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
	// }
	// putSuffix0()

	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	getBuckets := func(end bool) {
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n-1; i++ {
			bkt[s[i]]++
		}
		bkt[0]++
		if end {
			bkt[0]--
			for i := 1; i < K; i++ {
				bkt[i] += bkt[i-1]
			}
		} else {
			sum := 0
			for i := 0; i < K; i++ {
				sum += bkt[i]
				bkt[i] = sum - bkt[i]
			}
		}
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if j == len(s)-1 || s[j] >= s[j+1] {
					// if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					SA[bkt[s[i]]] = i
					bkt[s[i]]--
				}
				succT = false
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(pos int) int {
		if pos == n-1 {
			return 1
		}
		dist, i := 0, 1

		for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
			i++
		}

		for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
			if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}

		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)

			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}
		for i, j := n-1, n-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}
		return nameCtr
	}

	putSubstr0()
	induceSAl0(false)
	induceSAs0(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr()
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	// getSAlms := func() {
	j := n1 - 2
	s1[j+1] = n - 1
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				s1[j] = i
				j--
			}
			succT = false
		}
	}
	for i := 0; i < n1; i++ {
		SA[i] = s1[SA[i]]
	}
	for i := n1; i < n; i++ {
		SA[i] = 0

	}
	// }
	// getSAlms()

	// putSuffix0 := func() {
	getBuckets(true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
	// }
	// putSuffix0()

	induceSAl0(true)
	induceSAs0(true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648
const K = 256

func getBuckets(s []byte, bkt []int, n int, end bool) {
	for i := 0; i < K; i++ {
		bkt[i] = 0
	}
	for i := 0; i < n-1; i++ {
		bkt[s[i]]++
	}
	bkt[0]++
	if end {
		bkt[0]--
		for i := 1; i < K; i++ {
			bkt[i] += bkt[i-1]
		}
	} else {
		sum := 0
		for i := 0; i < K; i++ {
			sum += bkt[i]
			bkt[i] = sum - bkt[i]
		}
	}
}

func putSuffix0(s []byte, bkt, SA []int, n, n1 int) {
	getBuckets(s, bkt, n, true)
	for i := n1 - 1; i > 0; i-- {
		j := SA[i]
		SA[i] = 0
		SA[bkt[s[j]]] = j
		bkt[s[j]]--
	}
	SA[0] = n - 1
}

func induceSAl0(s []byte, bkt, SA []int, n int, suffix bool) {
	getBuckets(s, bkt, n, false)
	bkt[0]++
	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			j := SA[i] - 1
			if j == len(s)-1 || s[j] >= s[j+1] {
				// if s[j] >= s[j+1] {
				SA[bkt[s[j]]] = j
				bkt[s[j]]++
				if !suffix && i > 0 {
					SA[i] = 0
				}
			}
		}
	}
}

func induceSAs0(s []byte, bkt, SA []int, n int, suffix bool) {
	getBuckets(s, bkt, n, true)
	for i := n - 1; i > 0; i-- {
		if SA[i] > 0 {
			j := SA[i] - 1
			// if (j == len(s)-1 || s[j] <= s[j+1]) && bkt[s[j]] < i {
			if s[j] <= s[j+1] && bkt[s[j]] < i {
				SA[bkt[s[j]]] = j
				bkt[s[j]]--
				if !suffix {
					SA[i] = 0
				}
			}
		}
	}
}

func putSubstr0(s []byte, bkt, SA []int, n int) {
	getBuckets(s, bkt, n, true)
	for i := 0; i < n; i++ {
		SA[i] = 0
	}
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = false
		}
	}
	SA[0] = n - 1
}

func getLengthofLMS(s []byte, bkt, SA []int, n, pos int) int {
	if pos == n-1 {
		return 1
	}
	dist, i := 0, 1

	for pos+i < n-1 && s[pos+i] >= s[pos+i-1] {
		i++
	}

	for ; pos+i <= n-1 && (pos+i == n-1 || s[pos+i] <= s[pos+i-1]); i++ {
		if pos+i == n-1 || s[pos+i] < s[pos+i-1] {
			dist = i
		}
	}
	return dist + 1
}

func nameSubstr(s []byte, bkt, SA []int, n, n1 int) int {
	s1 := SA[n-n1:]
	for i := n1; i < n; i++ {
		SA[i] = empty
	}

	name, nameCtr := 0, 0
	prePos, preLen := 0, 0
	for i := 0; i < n1; i++ {
		diff := false
		pos := SA[i]
		length := getLengthofLMS(s, bkt, SA, n, pos)

		if length != preLen {
			diff = true
		} else {
			for d := 0; d < length; d++ {
				if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
					diff = true
					break
				}
			}
		}
		if diff {
			name = i
			nameCtr++
			SA[name] = 1
			prePos = pos
			preLen = length
		} else {
			SA[name]++
		}
		SA[n1+pos/2] = name
	}
	for i, j := n-1, n-1; i >= n1; i-- {
		if SA[i] != empty {
			SA[j] = SA[i]
			j--
		}
	}

	for succT, i := true, n1-1; i > 0; i-- {
		if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
			succT = true
			s1[i-1] += SA[s1[i-1]] - 1
		} else {
			succT = false
		}
	}
	return nameCtr
}

func getSAlms(s []byte, bkt, SA []int, n, n1 int) {
	j := n1 - 2
	s1 := SA[n-n1:]
	s1[j+1] = n - 1
	for succT, i := false, n-2; i > 0; i-- {
		if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
			succT = true
		} else {
			if succT {
				s1[j] = i
				j--
			}
			succT = false
		}
	}
	for i := 0; i < n1; i++ {
		SA[i] = s1[SA[i]]
	}
	for i := n1; i < n; i++ {
		SA[i] = 0

	}
}

func osaca1(s []byte) []int {
	n := len(s) + 1

	SA := make([]int, n)
	var bkt = make([]int, K)

	var s1 []int

	var n1 int = 0

	putSubstr0(s, bkt, SA, n)
	induceSAl0(s, bkt, SA, n, false)
	induceSAs0(s, bkt, SA, n, false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}

	s1 = SA[n-n1:]
	nameCtr := nameSubstr(s, bkt, SA, n, n1)
	if nameCtr < n1 {
		osaca2(s1, SA, n1, n-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA[s1[i]] = i
		}
	}

	getSAlms(s, bkt, SA, n, n1)

	putSuffix0(s, bkt, SA, n, n1)
	induceSAl0(s, bkt, SA, n, true)
	induceSAs0(s, bkt, SA, n, true)

	return SA[1:]

}

func osaca2(s, SA []int, n, m int) {
	var SA1, s1 []int

	var n1 int = 0

	putSuffix1 := func() {
		pos, pre := 0, -1
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = empty
			if s[j] != pre {
				pre = s[j]
				pos = s[j]
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step := 1
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			if s[j] < s[j+1] { //isL := c >= c1; !isL
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] - 1
				for ; SA[h] >= 0 || SA[h] == empty; h-- {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if s[j] < n-1 && SA[s[j]+1] == empty {
					SA[s[j]] = -1
					SA[s[j]+1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]+h] = SA[s[j]+h+1]
					}
					pos--
					if s[j] < i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}

			isL1 := (j+1 < n-1) &&
				(s[j+1] > s[j+2] || (s[j+1] == s[j+2] && s[j+1] < i))

			if (!suffix || !isL1) && i > 0 {
				if step == 0 {
					SA[i-1] = empty
				} else {
					SA[i] = empty
				}
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h := 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i-j] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step := 1
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}

			// isS := (s[j] < s[j+1]) || (s[j] == s[j+1] && s[j] > i)
			if (s[j] > s[j+1]) || (s[j] == s[j+1] && s[j] <= i) { //!isS
				continue
			}
			d := SA[s[j]]
			if d >= 0 {
				foo := SA[s[j]]
				h := s[j] + 1
				for ; SA[h] >= 0 || SA[h] == empty; h++ {
					SA[h], foo = foo, SA[h]
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[s[j]-1] == empty {
					SA[s[j]] = -1
					SA[s[j]-1] = j
				} else {
					SA[s[j]] = j
				}
			} else {
				pos := s[j] + d - 1
				if SA[pos] != empty {
					for h := 0; h < (-d); h++ {
						SA[s[j]-h] = SA[s[j]-h-1]
					}
					pos++
					if s[j] > i {
						step = 0
					}
				} else {
					SA[s[j]]--
				}
				SA[pos] = j
			}
			if !suffix {
				if step == 0 {
					SA[i+1] = empty
				} else {
					SA[i] = empty
				}
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h := 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i+j] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						SA[h], SA[c] = SA[c], SA[h]
					}
					SA[h] = SA[c]
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s[x+i] >= s[x+i-1] {
			i++
		}
		for ; x+i <= n-1 && s[x+i] <= s[x+i-1]; i++ {
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func() int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		for succT, i := true, n1-1; i > 0; i-- {
			if s1[i-1] < s1[i] || (s1[i-1] == s1[i] && succT) {
				succT = true
				s1[i-1] += SA[s1[i-1]] - 1
			} else {
				succT = false
			}
		}

		return nameCtr
	}

	getSAlms := func() {
		j := n1 - 2
		s1[j+1] = n - 1
		for succT, i := false, n-2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT) {
				succT = true
			} else {
				if succT {
					s1[j] = i
					j--
				}
				succT = false
			}
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			SA[i] = empty

		}
	}
	putSubstr1()
	induceSAl1(false)
	induceSAs1(false)

	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]

	nameCtr := nameSubstr()

	if nameCtr < n1 {
		osaca2(s1, SA1, n1, m-n1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms()

	putSuffix1()

	induceSAl1(true)
	induceSAs1(true)
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, osaca1(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648

func osaca(s, SA []int, n, K, m, level int) {
	var bkt []int

	var SA1, s1 []int

	var n1 int

	getBuckets := func(end bool) {
		sum := 0
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n; i++ {
			bkt[s[i]]++
		}
		for i := 0; i < K; i++ {
			sum += bkt[i]
			if end {
				bkt[i] = sum - 1
			} else {
				bkt[i] = sum - bkt[i]
			}
		}
	}

	putSuffix0 := func() {
		getBuckets(true)
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = 0
			SA[bkt[s[j]]] = j
			bkt[s[j]]--
		}
		SA[0] = n - 1
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s[j] >= s[j+1] {
					SA[bkt[s[j]]] = j
					bkt[s[j]]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s[j] <= s[j+1] && bkt[s[j]] < i {
					SA[bkt[s[j]]] = j
					bkt[s[j]]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				SA[bkt[s[i]]] = i
				bkt[s[i]]--
			}
			succT = curT
		}
		SA[0] = n - 1
	}

	putSuffix1 := func() {
		i, j, pos, cur, pre := 0, 0, 0, 0, -1
		for i = n1 - 1; i > 0; i-- {
			j = SA[i]
			SA[i] = empty
			cur = s[j]
			if cur != pre {
				pre = cur
				pos = cur
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step, h := 1, 0
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s[j], s[j+1]
			isL := c >= c1
			if !isL {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c - 1; SA[h] >= 0 || SA[h] == empty; h-- {
					bar := SA[h]
					SA[h] = foo //SA[c]
					foo = bar   //SA[c]=SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if c < n-1 && SA[c+1] == empty {
					SA[c] = -1
					SA[c+1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c+h] = SA[c+h+1]
					}
					pos--
					if c < i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			c2 := 0
			isL1 := j+1 < n-1
			if isL1 {
				c2 = s[j+2]
				isL1 = c1 > c2 || (c1 == c2 && c1 < i)
			}
			if (!suffix || !isL1) && i > 0 {
				i1 := i
				if step == 0 {
					i1 = i - 1
				}
				SA[i1] = empty
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i+h] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step, h := 1, 0
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s[j], s[j+1]
			isS := (c < c1) || (c == c1 && c > i)
			if !isS {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c + 1; SA[h] >= 0 || SA[h] == empty; h++ {
					bar := SA[h]
					SA[h] = foo
					foo = bar
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[c-1] == empty {
					SA[c] = -1
					SA[c-1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c + d - 1
				if SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c-h] = SA[c-h-1]
					}
					pos++
					if c > i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			if !suffix {
				i1 := i
				if step == 0 {
					i1 = i + 1
				}
				SA[i1] = empty
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h = 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i-h] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s[n-2]
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s[i-1]
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					foo := SA[c]
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						bar := SA[h]
						SA[h] = foo
						foo = bar
					}
					SA[h] = foo
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(level, x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for {
			if s[x+i] < s[x+i-1] {
				break
			}
			i++
		}
		for {
			if x+i > n-1 || (s[x+i] > s[x+i-1]) {
				break
			}
			if x+i == n-1 || s[x+i] < s[x+i-1] {
				dist = i
			}
			i++
		}
		return dist + 1
	}

	nameSubstr := func(m, level int) int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(level, pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s[pos+d] != s[prePos+d] {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		succT, curT := 1, 0

		for i := n1 - 1; i > 0; i-- {
			ch, ch1 := s1[i], s1[i-1]
			if ch1 < ch || (ch1 == ch && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 1 {
				s1[i-1] += SA[s1[i-1]] - 1
			}
			succT = curT
		}

		return nameCtr
	}

	getSAlms := func(level int) {
		j := n1 - 2
		s1[j+1] = n - 1
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s[i-1] < s[i] || (s[i-1] == s[i] && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				s1[j] = i
				j--
			}
			succT = curT
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			if level != 0 {
				SA[i] = empty
			} else {
				SA[i] = 0
			}
		}
	}

	if level == 0 {
		bkt = make([]int, K)
		putSubstr0()
		induceSAl0(false)
		induceSAs0(false)

	} else {
		putSubstr1()
		induceSAl1(false)
		induceSAs1(false)
	}

	n1 = 0
	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]
	nameCtr := nameSubstr(m, level)
	if nameCtr < n1 {
		osaca(s1, SA1, n1, 0, m-n1, level+1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms(level)

	if level == 0 {
		putSuffix0()
		induceSAl0(true)
		induceSAs0(true)
	} else {
		putSuffix1()
		induceSAl1(true)
		induceSAs1(true)

	}

}

func inducedSort2(s []byte) []int {
	a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
	for i := 0; i < len(s); i++ {
		a[i] = int(s[i])
	}
	osaca(a, sa, len(s)+1, 256, len(s)+1, 0)
	return sa[1:]
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, inducedSort2(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	index := New([]byte(next()))
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

//

package main

import (
  "bufio"
  "bytes"
  "io"
  "os"
  "sort"
  "strconv"
)

type Scanner struct {
  r            io.Reader
  split        bufio.SplitFunc
  maxTokenSize int
  token, buf   []byte
  start, end   int
}

func NewScanner(r io.Reader) *Scanner {
  return &Scanner{
    r:            r,
    split:        bufio.ScanWords,
    maxTokenSize: 16777216,
    buf:          make([]byte, 4096),
  }
}

func (s *Scanner) advance(n int) bool {
  if n < 0 || n > s.end-s.start {
    return false
  }
  s.start += n
  return true
}

func (s *Scanner) Text() string {
  return string(s.token)
}

func (s *Scanner) Scan() bool {
  for {
    if s.end > s.start {
      advance, token, err := s.split(s.buf[s.start:s.end], false)
      if err != nil || !s.advance(advance) {
        return false
      }
      s.token = token
      if token != nil {
        return true
      }
    }
    if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
      copy(s.buf, s.buf[s.start:s.end])
      s.end -= s.start
      s.start = 0
    }
    if s.end == len(s.buf) {
      if len(s.buf) >= s.maxTokenSize {
        return false
      }
      newSize := len(s.buf) * 2
      if newSize > s.maxTokenSize {
        newSize = s.maxTokenSize
      }
      newBuf := make([]byte, newSize)
      copy(newBuf, s.buf[s.start:s.end])
      s.buf = newBuf
      s.end -= s.start
      s.start = 0
      continue
    }
    for loop := 0; ; {
      n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
      s.end += n
      if n > 0 {
        break
      }
      loop++
      if loop > 100 {
        break
      }
    }
  }
}

func (s *Scanner) Split(split bufio.SplitFunc) {
  s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
  sc.Scan()
  n, _ := strconv.Atoi(sc.Text())
  return n
}

func ni64() int64 {
  sc.Scan()
  n, _ := strconv.ParseInt(sc.Text(), 10, 64)
  return n
}

func pri32(i ...int) {
  for _, ii := range i {
    w.WriteString(strconv.Itoa(ii))
    sp()
  }
}
func pri64(i ...int64) {
  for _, ii := range i {
    w.WriteString(strconv.FormatInt(ii, 10))
    sp()
  }
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

func getBuckets(s []int, alphaSize int, end bool) []int {
  bkt := make([]int, alphaSize)
  for i := 0; i < len(s); i++ {
    bkt[s[i]]++
  }
  sum := 0
  for i := 0; i < alphaSize; i++ {
    sum += bkt[i]
    if end {
      bkt[i] = sum
    } else {
      bkt[i] = sum - bkt[i]
    }
  }
  return bkt
}

func induceSAl(s []int, t []bool, SA []int, alphaSize int, end bool) {
  bkt := getBuckets(s, alphaSize+1, end)
  for i := 0; i < len(s); i++ {
    j := SA[i] - 1
    if j >= 0 && t[j] == false {
      SA[bkt[s[j]]] = j
      bkt[s[j]]++
    }
  }
}

func induceSAs(s []int, t []bool, SA []int, alphaSize int, end bool) {
  bkt := getBuckets(s, alphaSize+1, end)
  for i := len(s) - 1; i >= 0; i-- {
    j := SA[i] - 1
    if j >= 0 && t[j] == true {
      bkt[s[j]]--
      SA[bkt[s[j]]] = j
    }
  }
}

func isLMS(t []bool, pos int) bool {
  return pos > 0 && t[pos] == true && t[pos-1] == false
}

func sais(s, SA []int, alphaSize int) {
  n := len(s)
  t := make([]bool, n)
  t[n-1] = true
  t[n-2] = false
  for i := n - 3; i >= 0; i-- {
    if s[i] < s[i+1] || (s[i] == s[i+1] && t[i+1] == true) {
      t[i] = true
    } else {
      t[i] = false
    }
  }
  bkt := getBuckets(s, alphaSize+1, true)
  for i := 0; i < n; i++ {
    SA[i] = -1
  }
  for i := 1; i < n; i++ {
    if isLMS(t, i) {

      bkt[s[i]]--
      SA[bkt[s[i]]] = i
    }
  }

  induceSAl(s, t, SA, alphaSize, false)

  induceSAs(s, t, SA, alphaSize, true)

  n1 := 0
  for i := 0; i < n; i++ {
    if isLMS(t, SA[i]) {
      SA[n1] = SA[i]
      n1++
    }
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }

  name, prev := 0, -1
  for i := 0; i < n1; i++ {
    pos := SA[i]
    diff := false
    for d := 0; d < n; d++ {
      if prev == -1 || s[pos+d] != s[prev+d] || t[pos+d] != t[prev+d] {
        diff = true
        break
      } else if d > 0 && (isLMS(t, pos+d) || isLMS(t, prev+d)) {
        break
      }
    }
    if diff {
      name++
      prev = pos
    }
    if pos%2 == 0 {
      pos = pos / 2
    } else {
      pos = (pos - 1) / 2
    }
    SA[n1+pos] = name - 1
  }
  for i, j := n-1, n-1; i >= n1; i-- {
    if SA[i] >= 0 {
      SA[j] = SA[i]
      j--
    }
  }

  SA1, s1 := SA[:n1], SA[n-n1:]

  if name < n1 {

    sais(s1, SA1, name-1)
  } else {
    for i := 0; i < n1; i++ {
      SA1[s1[i]] = i
    }
  }
  bkt = getBuckets(s, alphaSize+1, true)
  for i, j := 1, 0; i < n; i++ {
    if isLMS(t, i) {
      s1[j] = i
      j++
    }
  }
  for i := 0; i < n1; i++ {
    SA1[i] = s1[SA1[i]]
  }
  for i := n1; i < n; i++ {
    SA[i] = -1
  }
  for i := n1 - 1; i >= 0; i-- {
    j := SA[i]
    SA[i] = -1
    bkt[s[j]]--
    SA[bkt[s[j]]] = j
  }
  induceSAl(s, t, SA, alphaSize, false)
  induceSAs(s, t, SA, alphaSize, true)
}

func SAIS(s []byte) []int {
  a, sa := make([]int, len(s)+1), make([]int, len(s)+1)
  for i := 0; i < len(s); i++ {
    a[i] = int(s[i])
  }
  sais(a, sa, 256)
  return sa[1:]
}

func getString(filename string) string {
  fi, err := os.Open(filename)
  if err != nil {
    panic(err)
  }
  defer func() {
    if err := fi.Close(); err != nil {
      panic(err)
    }
  }()
  r := bufio.NewReader(fi)
  s, _ := r.ReadString('\n')
  return s
}

type Index struct {
  data []byte
  sa   []int
}

func New(data []byte) *Index {
  return &Index{data, SAIS(data)}
}

func (x *Index) at(i int) []byte {
  return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
  i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
  j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
  return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
  if len(s) > 0 && n != 0 {
    matches := x.lookupAll(s)
    if n < 0 || len(matches) < n {
      n = len(matches)
    }
    if n > 0 {
      result = make([]int, n)
      copy(result, matches)
    }
  }
  return
}

func main() {
  index := New([]byte(next()))
  heavy := index.Lookup([]byte("heavy"), -1)
  metal := index.Lookup([]byte("metal"), -1)
  sort.Ints(heavy)
  sort.Ints(metal)
  ans := int64(0)
  cur := 0
  for i := 0; i < len(heavy); i++ {
    for cur < len(metal) && metal[cur] <= heavy[i]+4 {
      cur++
    }
    ans += int64(len(metal) - cur)
  }
  pri64(ans)
  w.Flush()
}

package main

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"sort"
	"strconv"
)

type Scanner struct {
	r            io.Reader
	split        bufio.SplitFunc
	maxTokenSize int
	token, buf   []byte
	start, end   int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        bufio.ScanWords,
		maxTokenSize: 16777216,
		buf:          make([]byte, 4096),
	}
}

func (s *Scanner) advance(n int) bool {
	if n < 0 || n > s.end-s.start {
		return false
	}
	s.start += n
	return true
}

func (s *Scanner) Text() string {
	return string(s.token)
}

func (s *Scanner) Scan() bool {
	for {
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], false)
			if err != nil || !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		for loop := 0; ; {
			n, _ := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				break
			}
		}
	}
}

func (s *Scanner) Split(split bufio.SplitFunc) {
	s.split = split
}

var w *bufio.Writer = bufio.NewWriter(os.Stdout)
var sc *Scanner = NewScanner(os.Stdin)

func next() string { sc.Scan(); return sc.Text() }
func nc() byte     { sc.Scan(); return sc.Text()[0] }

func ni32() int {
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	return n
}

func ni64() int64 {
	sc.Scan()
	n, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return n
}

func pri32(i ...int) {
	for _, ii := range i {
		w.WriteString(strconv.Itoa(ii))
		sp()
	}
}
func pri64(i ...int64) {
	for _, ii := range i {
		w.WriteString(strconv.FormatInt(ii, 10))
		sp()
	}
}
func sp()   { w.WriteByte(' ') }
func endl() { w.WriteByte('\n') }

const empty = -2147483648

type numericSlice interface {
	at(pos int) int
}

type byteSlice []byte

func (b byteSlice) at(pos int) int {
	return int(b[pos])
}

type intSlice []int

func (b intSlice) at(pos int) int {
	return b[pos]
}

func osacai(s numericSlice, SA []int, n, K, m, level int) {
	var bkt []int

	var SA1, s1 []int

	var n1 int

	getBuckets := func(end bool) {
		sum := 0
		for i := 0; i < K; i++ {
			bkt[i] = 0
		}
		for i := 0; i < n; i++ {
			bkt[s.at(i)]++
		}
		for i := 0; i < K; i++ {
			sum += bkt[i]
			if end {
				bkt[i] = sum - 1
			} else {
				bkt[i] = sum - bkt[i]
			}
		}
	}

	putSuffix0 := func() {
		getBuckets(true)
		for i := n1 - 1; i > 0; i-- {
			j := SA[i]
			SA[i] = 0
			SA[bkt[s.at(j)]] = j
			bkt[s.at(j)]--
		}
		SA[0] = n - 1
	}

	induceSAl0 := func(suffix bool) {
		getBuckets(false)
		bkt[0]++
		for i := 0; i < n; i++ {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s.at(j) >= s.at(j+1) {
					SA[bkt[s.at(j)]] = j
					bkt[s.at(j)]++
					if !suffix && i > 0 {
						SA[i] = 0
					}
				}
			}
		}
	}

	induceSAs0 := func(suffix bool) {
		getBuckets(true)
		for i := n - 1; i > 0; i-- {
			if SA[i] > 0 {
				j := SA[i] - 1
				if s.at(j) <= s.at(j+1) && bkt[s.at(j)] < i {
					SA[bkt[s.at(j)]] = j
					bkt[s.at(j)]--
					if !suffix {
						SA[i] = 0
					}
				}
			}
		}
	}

	putSubstr0 := func() {
		getBuckets(true)
		for i := 0; i < n; i++ {
			SA[i] = 0
		}
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s.at(i-1) < s.at(i) || (s.at(i-1) == s.at(i) && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				SA[bkt[s.at(i)]] = i
				bkt[s.at(i)]--
			}
			succT = curT
		}
		SA[0] = n - 1
	}

	putSuffix1 := func() {
		i, j, pos, cur, pre := 0, 0, 0, 0, -1
		for i = n1 - 1; i > 0; i-- {
			j = SA[i]
			SA[i] = empty
			cur = s.at(j)
			if cur != pre {
				pre = cur
				pos = cur
			}
			SA[pos] = j
			pos--
		}
	}

	induceSAl1 := func(suffix bool) {
		step, h := 1, 0
		for i := 0; i < n; i += step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s.at(j), s.at(j+1)
			isL := c >= c1
			if !isL {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c - 1; SA[h] >= 0 || SA[h] == empty; h-- {
					bar := SA[h]
					SA[h] = foo //SA[c]
					foo = bar   //SA[c]=SA[h]
				}
				SA[h] = foo
				if h < i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if c < n-1 && SA[c+1] == empty {
					SA[c] = -1
					SA[c+1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c - d + 1
				if pos > n-1 || SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c+h] = SA[c+h+1]
					}
					pos--
					if c < i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			c2 := 0
			isL1 := j+1 < n-1
			if isL1 {
				c2 = s.at(j + 2)
				isL1 = c1 > c2 || (c1 == c2 && c1 < i)
			}
			if (!suffix || !isL1) && i > 0 {
				i1 := i
				if step == 0 {
					i1 = i - 1
				}
				SA[i1] = empty
			}
		}

		for i := 1; i < n; i++ {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i+h] = SA[i+h+1]
				}
				SA[i+h] = empty
			}
		}

	}

	induceSAs1 := func(suffix bool) {
		step, h := 1, 0
		for i := n - 1; i > 0; i -= step {
			step = 1
			j := SA[i] - 1
			if SA[i] <= 0 {
				continue
			}
			c, c1 := s.at(j), s.at(j+1)
			isS := (c < c1) || (c == c1 && c > i)
			if !isS {
				continue
			}
			d := SA[c]
			if d >= 0 {
				foo := SA[c]
				for h = c + 1; SA[h] >= 0 || SA[h] == empty; h++ {
					bar := SA[h]
					SA[h] = foo
					foo = bar
				}
				SA[h] = foo
				if h > i {
					step = 0
				}
				d = empty
			}
			if d == empty {
				if SA[c-1] == empty {
					SA[c] = -1
					SA[c-1] = j
				} else {
					SA[c] = j
				}
			} else {
				pos := c + d - 1
				if SA[pos] != empty {
					for h = 0; h < (-d); h++ {
						SA[c-h] = SA[c-h-1]
					}
					pos++
					if c > i {
						step = 0
					}
				} else {
					SA[c]--
				}
				SA[pos] = j
			}
			if !suffix {
				i1 := i
				if step == 0 {
					i1 = i + 1
				}
				SA[i1] = empty
			}
		}
		if !suffix {
			for i := n - 1; i >= 0; i-- {
				j := SA[i]
				if j < 0 && j != empty {
					for h = 0; h < (-j); h++ {
						SA[i-h] = SA[i-h-1]
					}
					SA[i-h] = empty
				}
			}
		}
	}

	putSubstr1 := func() {
		h := 0
		for i := 0; i < n; i++ {
			SA[i] = empty
		}
		c, c1 := 0, s.at(n-2)
		t, t1 := false, false
		for i := n - 2; i > 0; i-- {
			c, t = c1, t1
			c1 = s.at(i - 1)
			t1 = c1 < c || (c1 == c && t)
			if t && !t1 {
				if SA[c] >= 0 {
					foo := SA[c]
					for h = c + 1; SA[h] >= 0 && SA[h] != empty; h++ {
						bar := SA[h]
						SA[h] = foo
						foo = bar
					}
					SA[h] = foo
					SA[c] = empty
				}
				d := SA[c]
				if d == empty {
					if SA[c-1] == empty {
						SA[c] = -1
						SA[c-1] = i
					} else {
						SA[c] = i
					}
				} else {
					pos := c + d - 1
					if SA[pos] != empty {
						for h = 0; h < (-d); h++ {
							SA[c-h] = SA[c-h-1]
						}
						pos++
					} else {
						SA[c]--
					}
					SA[pos] = i
				}
			}
		}
		for i := n - 1; i > 0; i-- {
			j := SA[i]
			if j < 0 && j != empty {
				for h = 0; h < (-j); h++ {
					SA[i-h] = SA[i-h-1]
				}
				SA[i-h] = empty
			}
		}
		SA[0] = n - 1
	}

	getLengthofLMS := func(level, x int) int {
		if x == n-1 {
			return 1
		}
		dist, i := 0, 1
		for s.at(x+i) >= s.at(x+i-1) {
			i++
		}
		for ; x+i <= n-1 && s.at(x+i) <= s.at(x+i-1); i++ {
			if x+i == n-1 || s.at(x+i) < s.at(x+i-1) {
				dist = i
			}
		}
		return dist + 1
	}

	nameSubstr := func(m, level int) int {
		for i := n1; i < n; i++ {
			SA[i] = empty
		}
		name, nameCtr := 0, 0
		prePos, preLen := 0, 0
		for i := 0; i < n1; i++ {
			diff := false
			pos := SA[i]
			length := getLengthofLMS(level, pos)
			if length != preLen {
				diff = true
			} else {
				for d := 0; d < length; d++ {
					if pos+d == n-1 || prePos+d == n-1 || s.at(pos+d) != s.at(prePos+d) {
						diff = true
						break
					}
				}
			}
			if diff {
				name = i
				nameCtr++
				SA[name] = 1
				prePos = pos
				preLen = length
			} else {
				SA[name]++
			}
			SA[n1+pos/2] = name
		}

		for i, j := n-1, m-1; i >= n1; i-- {
			if SA[i] != empty {
				SA[j] = SA[i]
				j--
			}
		}

		succT, curT := 1, 0

		for i := n1 - 1; i > 0; i-- {
			ch, ch1 := s1[i], s1[i-1]
			if ch1 < ch || (ch1 == ch && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 1 {
				s1[i-1] += SA[s1[i-1]] - 1
			}
			succT = curT
		}

		return nameCtr
	}

	getSAlms := func(level int) {
		j := n1 - 2
		s1[j+1] = n - 1
		succT, curT := 0, 0
		for i := n - 2; i > 0; i-- {
			if s.at(i-1) < s.at(i) || (s.at(i-1) == s.at(i) && succT == 1) {
				curT = 1
			} else {
				curT = 0
			}
			if curT == 0 && succT == 1 {
				s1[j] = i
				j--
			}
			succT = curT
		}
		for i := 0; i < n1; i++ {
			SA[i] = s1[SA[i]]
		}
		for i := n1; i < n; i++ {
			if level != 0 {
				SA[i] = empty
			} else {
				SA[i] = 0
			}
		}
	}

	if level == 0 {
		bkt = make([]int, K)
		putSubstr0()
		induceSAl0(false)
		induceSAs0(false)

	} else {
		putSubstr1()
		induceSAl1(false)
		induceSAs1(false)
	}

	n1 = 0
	for i := 0; i < n; i++ {
		if SA[i] > 0 {
			SA[n1] = SA[i]
			n1++
		}
	}
	SA1, s1 = SA, SA[m-n1:]
	nameCtr := nameSubstr(m, level)
	if nameCtr < n1 {
		osacai(intSlice(s1), SA1, n1, 0, m-n1, level+1)
	} else {
		for i := 0; i < n1; i++ {
			SA1[s1[i]] = i
		}
	}

	getSAlms(level)

	if level == 0 {
		putSuffix0()
		induceSAl0(true)
		induceSAs0(true)
	} else {
		putSuffix1()
		induceSAl1(true)
		induceSAs1(true)

	}

}

func inducedSort2(s []byte) []int {
	sa := make([]int, len(s))
	osacai(byteSlice(s), sa, len(s), 256, len(s), 0)
	return sa[1:]
}

type Index struct {
	data []byte
	sa   []int
}

func New(data []byte) *Index {
	return &Index{data, inducedSort2(data)}
}

func (x *Index) at(i int) []byte {
	return x.data[x.sa[i]:]
}

func (x *Index) lookupAll(s []byte) []int {
	i := sort.Search(len(x.sa), func(i int) bool { return bytes.Compare(x.at(i), s) >= 0 })
	j := i + sort.Search(len(x.sa)-i, func(j int) bool { return !bytes.HasPrefix(x.at(j+i), s) })
	return x.sa[i:j]
}

func (x *Index) Lookup(s []byte, n int) (result []int) {
	if len(s) > 0 && n != 0 {
		matches := x.lookupAll(s)
		if n < 0 || len(matches) < n {
			n = len(matches)
		}
		if n > 0 {
			result = make([]int, n)
			copy(result, matches)
		}
	}
	return
}

func main() {
	s := []byte(next())
	s = append(s, '0')
	index := New(s)
	heavy := index.Lookup([]byte("heavy"), -1)
	metal := index.Lookup([]byte("metal"), -1)
	sort.Ints(heavy)
	sort.Ints(metal)
	ans := int64(0)
	cur := 0
	for i := 0; i < len(heavy); i++ {
		for cur < len(metal) && metal[cur] <= heavy[i]+4 {
			cur++
		}
		ans += int64(len(metal) - cur)
	}
	pri64(ans)
	w.Flush()
}

// 318B-mic
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    z := bufio.NewReader(os.Stdin)
    var s string
    var dem, d int64
    fmt.Fscan(z, &s)
    for i := len(s) - 4; i >= 0; i-- {
        if s[i] == 'h' && s[i+1] == 'e' && s[i+2] == 'a' && s[i+3] == 'v' && s[i+4] == 'y' {
            dem += d
        }
        if s[i] == 'm' && s[i+1] == 'e' && s[i+2] == 't' && s[i+3] == 'a' && s[i+4] == 'l' {
            d++
        }
    }
    fmt.Println(dem)
}

package main

import "fmt"

func main() {
	var str string
	fmt.Scan(&str)
	r := []rune(str)
  heavy := find(r, "heavy")
	metal := find(r, "metal")
	count := uint64(0)
	i := 0
	for _, h := range heavy {
		for i < len(metal) && metal[i] < h {
			i++
		}
		count += uint64(len(metal) - i)
	}
	fmt.Println(count)
}

func find(r []rune, s string) []int {
	ret := make([]int, 0)
	for i := 0; i < len(r); i++ {
		found := true
		for j, v := range s {
			if i+j >= len(r) || r[i+j] != v {
				found = false
				break
			}
		}
		if found {
			ret = append(ret, i)
			i += len(s) - 1
		}
	}
	return ret
}

package main

import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    var s string
    fmt.Fscan(reader,&s)
    ans, heavy := 0, 0
    for i := 0; i <= (len(s)-5); i++ {
        t := s[i:i+5]
        if t == "heavy" {
            heavy++
        } else if t == "metal" {
            ans += heavy
        }
    }
    fmt.Println(ans)
}

// 318B-mic
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    z := bufio.NewReader(os.Stdin)
    var s string
    var dem, d int
    fmt.Fscan(z, &s)
    for i := len(s) - 4; i >= 0; i-- {
        if s[i] == 'h' && s[i+1] == 'e' && s[i+2] == 'a' && s[i+3] == 'v' && s[i+4] == 'y' {
            dem += d
        }
        if s[i] == 'm' && s[i+1] == 'e' && s[i+2] == 't' && s[i+3] == 'a' && s[i+4] == 'l' {
            d++
        }
    }
    fmt.Println(dem)
}

package main

import "fmt"

func main() {
	var str string
	fmt.Scan(&str)
	r := []rune(str)
  heavy := find(r, "heavy")
	metal := find(r, "metal")
	count := 0
	i := 0
	for _, h := range heavy {
		for i < len(metal) && metal[i] < h {
			i++
		}
		count += len(metal) - i
	}
	fmt.Println(count)
}

func find(r []rune, s string) []int {
	ret := make([]int, 0)
	for i := 0; i < len(r); i++ {
		found := true
		for j, v := range s {
			if i+j >= len(r) || r[i+j] != v {
				found = false
				break
			}
		}
		if found {
			ret = append(ret, i)
			i += len(s) - 1
		}
	}
	return ret
}