← Home
package main

import (
	"io"
	"os"
	"strconv"
)

type FastScanner struct {
	data []byte
	idx  int
	n    int
}

func NewFastScanner() *FastScanner {
	data, _ := io.ReadAll(os.Stdin)
	return &FastScanner{data: data, n: len(data)}
}

func (fs *FastScanner) skip() {
	for fs.idx < fs.n {
		b := fs.data[fs.idx]
		if b == ' ' || b == '\n' || b == '\r' || b == '\t' {
			fs.idx++
		} else {
			break
		}
	}
}

func (fs *FastScanner) nextInt() int {
	fs.skip()
	val := 0
	for fs.idx < fs.n {
		b := fs.data[fs.idx]
		if b < '0' || b > '9' {
			break
		}
		val = val*10 + int(b-'0')
		fs.idx++
	}
	return val
}

func (fs *FastScanner) nextBytes() []byte {
	fs.skip()
	start := fs.idx
	for fs.idx < fs.n {
		b := fs.data[fs.idx]
		if b == ' ' || b == '\n' || b == '\r' || b == '\t' {
			break
		}
		fs.idx++
	}
	return fs.data[start:fs.idx]
}

func main() {
	fs := NewFastScanner()
	n := fs.nextInt()
	m := fs.nextInt()

	grid := make([][]byte, n)
	for i := 0; i < n; i++ {
		grid[i] = fs.nextBytes()
	}

	down := make([][]int, n)
	for i := 0; i < n; i++ {
		down[i] = make([]int, m)
	}

	for j := 0; j < m; j++ {
		down[n-1][j] = 1
		for i := n - 2; i >= 0; i-- {
			if grid[i][j] == grid[i+1][j] {
				down[i][j] = down[i+1][j] + 1
			} else {
				down[i][j] = 1
			}
		}
	}

	var ans int64

	for i := 0; i < n; i++ {
		run := 0
		prevValid := false
		prevH := 0
		var pa, pb, pc byte

		for j := 0; j < m; j++ {
			h := down[i][j]
			i2 := i + h
			i3 := i + 2*h

			valid := false
			var a, b, c byte

			if i3 < n && down[i2][j] == h && down[i3][j] >= h {
				a = grid[i][j]
				b = grid[i2][j]
				c = grid[i3][j]
				if a != b && b != c {
					valid = true
				}
			}

			if valid {
				if prevValid && prevH == h && pa == a && pb == b && pc == c {
					run++
				} else {
					run = 1
					prevValid = true
					prevH = h
					pa, pb, pc = a, b, c
				}
				ans += int64(run)
			} else {
				run = 0
				prevValid = false
			}
		}
	}

	os.Stdout.WriteString(strconv.FormatInt(ans, 10))
}