import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.readline

INF = 10**9

n, m = [int(item) for item in input().split()]
s = input().rstrip()

count = [[0] * m for _ in range(m)]
ord_a = ord("a")
for c1, c2 in zip(s, s[1:]):
    c1 = ord(c1) - ord_a
    c2 = ord(c2) - ord_a
    if c1 != c2:
        count[c1][c2] += 1

sum_of_subset = [[0] * (1 << m) for _ in range(m)]
for i in range(m):
    for j in range(1 << m):
        if j == 0:
            continue
        lsb = j & -j
        sum_of_subset[i][j] = sum_of_subset[i][j ^ lsb] + count[i][lsb.bit_length() - 1]

adj_in_subset = [0] * (1 << m)
for i in range(1 << m):
    for j in range(m):
        if i & (1 << j):
            adj_in_subset[i] += sum_of_subset[j][i]

total_adj = adj_in_subset[-1]
dp = [INF] * (1 << m)
dp[0] = 0

for i in range(1 << m):
    for j in range(m):
        if i & 1 << j:
            continue
        not_i = ((1 << m) - 1) ^ i
        val = dp[i] + (total_adj - adj_in_subset[i] - adj_in_subset[not_i])
        dp[i | (1 << j)] = min(dp[i | (1 << j)], val)
print(dp[-1])


import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.readline

INF = 10**9

n, m = [int(item) for item in input().split()]
s = input().rstrip()

count = [[0] * m for _ in range(m)]
ord_a = ord("a")
for c1, c2 in zip(s, s[1:]):
    c1 = ord(c1) - ord_a
    c2 = ord(c2) - ord_a
    if c1 != c2:
        count[c1][c2] += 1

sum_of_subset = [[0] * (1 << m) for _ in range(m)]
for i in range(m):
    for j in range(1 << m):
        if j == 0:
            continue
        lsb = j & -j
        sum_of_subset[i][j] = sum_of_subset[i][j ^ lsb] + count[i][lsb.bit_length() - 1]

adj_in_subset = [0] * (1 << m)
for i in range(1 << m):
    for j in range(m):
        if i & (1 << j):
            adj_in_subset[i] += sum_of_subset[j][i]

total_adj = adj_in_subset[-1]
dp = [INF] * (1 << m)
dp[0] = 0

for i in range(1 << m):
    for j in range(m):
        if i & 1 << j:
            continue
        cost = total_adj - adj_in_subset[i] - adj_in_subset[~i]
        dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + cost)
print(dp[-1])


import os
import sys
from io import BytesIO, IOBase
# region fastio

BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip("\r\n")

# ------------------------------

def RL(): return map(int, sys.stdin.readline().rstrip().split())
def RLL(): return list(map(int, sys.stdin.readline().rstrip().split()))
def N(): return int(input())
def comb(n, m): return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0
def perm(n, m): return factorial(n) // (factorial(n - m)) if n >= m else 0
def mdis(x1, y1, x2, y2): return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

# ------------------------------

def cord(c):
    return ord(c)-ord('a')

def main():
    n, m = RL()
    s = input()
    inf = m*m
    ct = [0]*(1<<m)

    for i in range(n-1):
        now, nex = cord(s[i]), cord(s[i+1])
        if now==nex: continue
        ct[1<<now | 1<<nex]+=1

    for i in range(m):
        for j in range(1<<m):
            if 1<<i & j:
                ct[j]+=ct[(1<<i) ^ j]

    dp = [inf]*(1<<m)
    dp[0] = 0
    for i in range(1<<m):
        # pos = bin(i).count('1')
        for j in range(m):
            # sm = 0
            # if i>>j & 1 == 0:
            #     for k in range(m):
            #         if 1<<k & i==0:
            #             sm-=ct[j][k]*pos
            #         else:
            #             sm+=ct[j][k]*pos
            if i & 1<<j == 0:
                sm = ct[-1]-ct[i]-ct[~i]
                dp[i|1<<j] = min(dp[i|1<<j], dp[i] + sm)

    print(dp[-1])

if __name__ == "__main__":
    main()


import os
import sys
from io import BytesIO, IOBase
# region fastio

BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip("\r\n")

# ------------------------------

def RL(): return map(int, sys.stdin.readline().rstrip().split())
def RLL(): return list(map(int, sys.stdin.readline().rstrip().split()))
def N(): return int(input())
def comb(n, m): return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0
def perm(n, m): return factorial(n) // (factorial(n - m)) if n >= m else 0
def mdis(x1, y1, x2, y2): return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

# ------------------------------

def cord(c):
    return ord(c)-ord('a')

def main():
    n, m = RL()
    s = input()
    ct = [0]*(1<<m)

    for i in range(n-1):
        now, nex = cord(s[i]), cord(s[i+1])
        if now==nex: continue
        ct[1<<now | 1<<nex]+=1

    for i in range(m):
        for j in range(1<<m):
            if (1<<i) & j:
                ct[j]+=ct[(1<<i) ^ j]

    dp = [INF]*(1<<m)
    dp[0] = 0
    for i in range(1<<m):
        # pos = bin(i).count('1')
        for j in range(m):
            # sm = 0
            # if i>>j & 1 == 0:
            #     for k in range(m):
            #         if 1<<k & i==0:
            #             sm-=ct[j][k]*pos
            #         else:
            #             sm+=ct[j][k]*pos
            if i & 1<<j == 0:
                sm = ct[-1]-ct[i]-ct[~i]
                dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + sm)
    # print(ct)
    print(dp[-1])

if __name__ == "__main__":
    main()


import os
import sys
from io import BytesIO, IOBase
# region fastio

BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip("\r\n")

# ------------------------------

def RL(): return map(int, sys.stdin.readline().rstrip().split())
def RLL(): return list(map(int, sys.stdin.readline().rstrip().split()))
def N(): return int(input())
def comb(n, m): return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0
def perm(n, m): return factorial(n) // (factorial(n - m)) if n >= m else 0
def mdis(x1, y1, x2, y2): return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

# ------------------------------

def cord(c):
    return ord(c)-ord('a')

def main():
    n, m = RL()
    s = input()
    inf = m*m
    ct = [0]*(1<<m)

    for i in range(n-1):
        now, nex = cord(s[i]), cord(s[i+1])
        if now==nex: continue
        ct[1<<now | 1<<nex]+=1

    for i in range(m):
        for j in range(1<<m):
            if (1<<i) & j:
                ct[j]+=ct[(1<<i) ^ j]

    dp = [inf]*(1<<m)
    dp[0] = 0
    for i in range(1<<m):
        # pos = bin(i).count('1')
        for j in range(m):
            # sm = 0
            # if i>>j & 1 == 0:
            #     for k in range(m):
            #         if 1<<k & i==0:
            #             sm-=ct[j][k]*pos
            #         else:
            #             sm+=ct[j][k]*pos
            if i & 1<<j == 0:
                sm = ct[-1]-ct[i]-ct[~i]
                dp[i|1<<j] = min(dp[i|1<<j], dp[i] + sm)
    print(ct)
    print(dp[-1])

if __name__ == "__main__":
    main()


import os
import sys
from io import BytesIO, IOBase
# region fastio

BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip("\r\n")

# ------------------------------

def RL(): return map(int, sys.stdin.readline().rstrip().split())
def RLL(): return list(map(int, sys.stdin.readline().rstrip().split()))
def N(): return int(input())
def comb(n, m): return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0
def perm(n, m): return factorial(n) // (factorial(n - m)) if n >= m else 0
def mdis(x1, y1, x2, y2): return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

# ------------------------------

def cord(c):
    return ord(c)-ord('a')

def main():
    n, m = RL()
    s = input()
    inf = m*m
    ct = [0]*(1<<m)

    for i in range(n-1):
        now, nex = cord(s[i]), cord(s[i+1])
        if now==nex: continue
        ct[1<<now | 1<<nex]+=1

    for i in range(m):
        for j in range(1<<m):
            if (1<<i) & j:
                ct[j]+=ct[(1<<i) ^ j]

    dp = [inf]*(1<<m)
    dp[0] = 0
    for i in range(1<<m):
        # pos = bin(i).count('1')
        for j in range(m):
            # sm = 0
            # if i>>j & 1 == 0:
            #     for k in range(m):
            #         if 1<<k & i==0:
            #             sm-=ct[j][k]*pos
            #         else:
            #             sm+=ct[j][k]*pos
            if i & 1<<j == 0:
                sm = ct[-1]-ct[i]-ct[~i]
                dp[i|1<<j] = min(dp[i|1<<j], dp[i] + sm)
    # print(ct)
    print(dp[-1])

if __name__ == "__main__":
    main()


import os
import sys
from io import BytesIO, IOBase
# region fastio

BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)

class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")

sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)
input = lambda: sys.stdin.readline().rstrip("\r\n")

# ------------------------------

def RL(): return map(int, sys.stdin.readline().rstrip().split())
def RLL(): return list(map(int, sys.stdin.readline().rstrip().split()))
def N(): return int(input())
def comb(n, m): return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0
def perm(n, m): return factorial(n) // (factorial(n - m)) if n >= m else 0
def mdis(x1, y1, x2, y2): return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

# ------------------------------

def cord(c):
    return ord(c)-ord('a')

def main():
    n, m = RL()
    s = input()
    inf = m*m
    ct = [[0]*(m+1) for _ in range(m+1)]

    for i in range(n-1):
        now, nex = cord(s[i]), cord(s[i+1])
        if now==nex: continue
        ct[now][nex]+=1
        ct[nex][now]+=1

    dp = [inf]*(1<<m)
    dp[0] = 0
    for i in range(1<<m):
        pos = bin(i).count('1')
        for j in range(m):
            sm = 0
            if i>>j & 1 == 0:
                for k in range(m):
                    if 1<<k & i==0:
                        sm-=ct[j][k]*pos
                    else:
                        sm+=ct[j][k]*pos
            dp[i|1<<j] = min(dp[i|1<<j], dp[i] + sm)

    print(dp[-1])

if __name__ == "__main__":
    main()


n, m = map(int, input().split())
a = list(map(str, input().strip()))

dp = [10 ** 10] * (1 << 20)
cnt = [0] * (1 << 20)

def get(x):
    return 1 << (ord(x) - ord('a'))

for i, v in enumerate(a):
    if i:
        cnt[get(a[i]) | get(a[i - 1])] += 1

for i in range(m):
    for j in range(1 << m):
        if (1 << i) & j:
            cnt[j] += cnt[j ^ (1 << i)]
            # print(bin(j), bin(j ^ 1 << i), cnt[j])

# for i in range(1 << m):
#     for j in range(m):
#         if not i & (1 << j):
#             cnt[i | (1 << j)] += cnt[i]
#             print(bin(i | (1 << j)), bin(i), cnt[i | 1 << j])

dp[0] = 0

for i in range(1 << m):
    for j in range(m):
        if not i & (1 << j):
            dp[i | (1 << j)] = min(dp[i | (1 << j)],
                                   dp[i] + n - 1 - cnt[i | (1 << j)] - cnt[(1 << m) - 1 - (i | (1 << j))])
print(dp[(1 << m) - 1])


@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_FEATURE_WARNING", "OVERRIDE_BY_INLINE")
@file:OptIn(ExperimentalStdlibApi::class)

import java.io.PrintWriter
import kotlin.math.*
import kotlin.random.Random
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending
import kotlin.io.println as iprintln

/** @author Spheniscine */
fun main() { _writer.solve(); _writer.flush() }
fun PrintWriter.solve() {
//    val startTime = System.nanoTime()

    val numCases = 1//readInt()
    case@ for(case in 1..numCases) {
        //print("Case #$case: ")

        val n = readInt()
        val m = readInt()
        val s = readCharArray(n)

        val co = Array(m) { IntArray(m) }

        for(i in 1 until n) {
            val a = s[i-1] - 'a'
            val b = s[i] - 'a'
            co[a][b]++
            co[b][a]++
        }

        val dp = IntArray(1 shl m) { inf }
        dp[0] = 0

        for(mask in 1 until dp.size) {
            val pos = mask.countOneBits()
            for(x in 0 until m) {
                if(mask.containsBit(x)) {
                    var sum = dp[mask.flipBit(x)]
                    for(y in 0 until m) {
                        if(x == y) continue
                        val v = co[x][y] * pos
                        if(mask.containsBit(y)) sum += v else sum -= v
                    }
                    dp.setMin(mask, sum)
                }
            }
        }

        val ans = dp.last()
        println(ans)
    }

//    iprintln("Time: ${(System.nanoTime() - startTime) / 1000000} ms")
}

const val inf = Int.MAX_VALUE
fun IntArray.setMin(i: Int, v: Int) { if(v < this[i]) this[i] = v }
inline fun Int.containsBit(x: Int) = this ushr x and 1 == 1
inline fun Int.flipBit(x: Int) = this xor (1 shl x)

/** IO */
//@JvmField val ONLINE_JUDGE = System.getProperty("ONLINE_JUDGE") != null
//const val PATH = "src/main/resources/"
//@JvmField val INPUT = File(PATH + "input.txt").inputStream()
//@JvmField val OUTPUT = File(PATH + "output.txt").outputStream()
@JvmField val INPUT = System.`in`
@JvmField val OUTPUT = System.out

const val _BUFFER_SIZE = 1 shl 16
@JvmField val _buffer = ByteArray(_BUFFER_SIZE)
@JvmField var _bufferPt = 0
@JvmField var _bytesRead = 0

tailrec fun readChar(): Char {
    if(_bufferPt == _bytesRead) {
        _bufferPt = 0
        _bytesRead = INPUT.read(_buffer, 0, _BUFFER_SIZE)
    }
    return if(_bytesRead < 0) Char.MIN_VALUE
    else {
        val c = _buffer[_bufferPt++].toChar()
        if (c == '\r') readChar() else c
    }
}

/** @param skipNext Whether to skip the next character (usually whitespace), defaults to true */
fun readCharArray(n: Int, skipNext: Boolean = true): CharArray {
    val res = CharArray(n) { readChar() }
    if(skipNext) readChar()
    return res
}

fun readLine(): String? {
    var c = readChar()
    return if(c == Char.MIN_VALUE) null
    else buildString {
        while(c != '\n' && c != Char.MIN_VALUE) {
            append(c)
            c = readChar()
        }
    }
}
fun readLn() = readLine()!!

fun read() = buildString {
    var c = readChar()
    while(c <= ' ') {
        if(c == Char.MIN_VALUE) return@buildString
        c = readChar()
    }
    do {
        append(c)
        c = readChar()
    } while(c > ' ')
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readLines(n: Int) = List(n) { readLn() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(OUTPUT, false)

/**  sort overrides to avoid quicksort attacks */

@JvmField var _random: Random? = null
val random get() = _random ?: Random(0x594E215C123 * System.nanoTime()).also { _random = it }

inline fun <A, T> _mergeSort(a0: A, n: Int, tmp0: A, get: A.(Int) -> T, set: A.(Int, T) -> Unit, cmp: (T, T) -> Int) {
    var a = a0
    var tmp = tmp0
    var len = 1
    while(len < n) {
        var l = 0
        while(true) {
            val m = l + len
            if(m >= n) break
            val r = min(n, m + len)
            var i = l
            var j = m
            for(k in l until r) {
                if(i != m && (j == r || cmp(a.get(i), a.get(j)) <= 0)) {
                    tmp.set(k, a.get(i++))
                } else tmp.set(k, a.get(j++))
            }
            l = r
        }
        for(i in l until n) tmp.set(i, a.get(i))
        val t = a; a = tmp; tmp = t
        len += len
    }
    if(a !== a0) for(i in 0 until n) a0.set(i, tmp0.get(i))
}

inline fun IntArray.sortWith(cmp: (Int, Int) -> Int) { _mergeSort(this, size, IntArray(size), IntArray::get, IntArray::set, cmp) }
inline fun <T: Comparable<T>> IntArray.sortBy(func: (Int) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> IntArray.sortByDescending(func: (Int) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun IntArray.sort() { sortBy { it } }
fun IntArray.sortDescending() { sortByDescending { it } }

inline fun LongArray.sortWith(cmp: (Long, Long) -> Int) { _mergeSort(this, size, LongArray(size), LongArray::get, LongArray::set, cmp) }
inline fun <T: Comparable<T>> LongArray.sortBy(func: (Long) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> LongArray.sortByDescending(func: (Long) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun LongArray.sort() { sortBy { it } }
fun LongArray.sortDescending() { sortByDescending { it } }

inline fun DoubleArray.sortWith(cmp: (Double, Double) -> Int) { _mergeSort(this, size, DoubleArray(size), DoubleArray::get, DoubleArray::set, cmp) }
inline fun <T: Comparable<T>> DoubleArray.sortBy(func: (Double) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> DoubleArray.sortByDescending(func: (Double) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun DoubleArray.sort() { sortBy { it } }
fun DoubleArray.sortDescending() { sortByDescending { it } }

inline fun CharArray.sort() { _sort() }
inline fun CharArray.sortDescending() { _sortDescending() }

inline fun <T : Comparable<T>> Array<out T>.sort() = _sort()
inline fun <T : Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
inline fun <T : Comparable<T>> MutableList<out T>.sort() = _sort()
inline fun <T : Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()

// import preserving junk function
@Suppress("NonAsciiCharacters") fun 雪花飄飄北風嘯嘯天地一片蒼茫() { iprintln(max(1, 2)) }

fun IntArray.sumLong() = sumOf { it.toLong() }

fun IntArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun IntArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun LongArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun LongArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun DoubleArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun DoubleArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> List<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> List<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }

// max/min Kotlin 1.6 -> 1.4 shim
fun IntArray.max() = maxOf { it }
fun IntArray.min() = minOf { it }
fun LongArray.max() = maxOf { it }
fun LongArray.min() = minOf { it }
fun CharArray.max() = maxOf { it }
fun CharArray.min() = minOf { it }
fun <T : Comparable<T>> Iterable<T>.max() = maxOf { it }
fun <T : Comparable<T>> Iterable<T>.min() = minOf { it }
fun <T : Comparable<T>> Sequence<T>.max() = maxOf { it }
fun <T : Comparable<T>> Sequence<T>.min() = minOf { it }


@file:Suppress("NOTHING_TO_INLINE")

import java.io.PrintWriter
import java.util.StringTokenizer
import kotlin.math.*
import kotlin.random.*
import kotlin.system.*
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending

fun main() {
    output {
        val n = readInt()
        val m = readInt()
        val s = readLn()

        val co = Array(m) { IntArray(m) }

        for(i in 1 until n) {
            val a = s[i-1]
            val b = s[i]
            co[a - 'a'][b - 'a']++
            co[b - 'a'][a - 'a']++
        }

        val dp = IntArray(1 shl m) { inf }
        dp[0] = 0

        for(mask in 1 until dp.size) {
            val pos = mask.bitCount()
            for(x in 0 until m) {
                if(mask.containsBit(x)) {
                    var sum = dp[mask.flipBit(x)]
                    for(y in 0 until m) {
                        if(x == y) continue
                        val v = co[x][y] * pos
                        if(mask.containsBit(y)) sum += v else sum -= v
                    }
                    dp.setMin(mask, sum)
                }
            }
        }

        val ans = dp.last()

        println(ans)
    }
}

const val inf = Int.MAX_VALUE
fun IntArray.setMin(i: Int, v: Int) { if(v < this[i]) this[i] = v }
inline fun Int.bitCount() = Integer.bitCount(this)
inline fun Int.containsBit(x: Int) = this ushr x and 1 == 1
inline fun Int.flipBit(x: Int) = this xor (1 shl x)

/** IO code start */
@JvmField val _reader = System.`in`.bufferedReader()
fun readLine(): String? = _reader.readLine()
fun readLn() = _reader.readLine()!!
@JvmField var _tokenizer: StringTokenizer = StringTokenizer("")
fun read(): String {
    while (_tokenizer.hasMoreTokens().not()) _tokenizer = StringTokenizer(_reader.readLine() ?: return "", " ")
    return _tokenizer.nextToken()
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(System.out, false)
inline fun output(block: PrintWriter.()->Unit) { _writer.apply(block).flush() }
inline fun iprintln(o: Any?) { println(o) } // immediate println for interactive, bypasses output{} blocks

/** shuffles and sort overrides to avoid quicksort attacks */
private inline fun <R, V> R._shuffle(rnd: Random, get: R.(Int) -> V, set: R.(Int, V) -> Unit, size: Int) {
    // Fisher-Yates shuffle algorithm
    for (i in size - 1 downTo 1) {
        val j = rnd.nextInt(i + 1)
        val temp = get(i)
        set(i, get(j))
        set(j, temp)
    }
}

@JvmField val random = Random(System.nanoTime())

fun IntArray.shuffle(rnd: Random = random) = _shuffle(rnd, IntArray::get, IntArray::set, size)
fun IntArray.sort() { shuffle(); _sort() }
fun IntArray.sortDescending() { shuffle(); _sortDescending() }

fun LongArray.shuffle(rnd: Random = random) = _shuffle(rnd, LongArray::get, LongArray::set, size)
fun LongArray.sort() { shuffle(); _sort() }
fun LongArray.sortDescending() { shuffle(); _sortDescending() }

fun DoubleArray.shuffle(rnd: Random = random) = _shuffle(rnd, DoubleArray::get, DoubleArray::set, size)
fun DoubleArray.sort() { shuffle(); _sort() }
fun DoubleArray.sortDescending() { shuffle(); _sortDescending() }

fun CharArray.shuffle(rnd: Random = random) = _shuffle(rnd, CharArray::get, CharArray::set, size)
inline fun CharArray.sort() { _sort() }
inline fun CharArray.sortDescending() { _sortDescending() }

inline fun <T: Comparable<T>> Array<out T>.sort() = _sort()
inline fun <T: Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
inline fun <T: Comparable<T>> MutableList<out T>.sort() = _sort()
inline fun <T: Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()


@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_FEATURE_WARNING", "OVERRIDE_BY_INLINE")
@file:OptIn(ExperimentalStdlibApi::class)

import java.io.PrintWriter
import kotlin.math.*
import kotlin.random.Random
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending
import kotlin.io.println as iprintln

/** @author Spheniscine */
fun main() { _writer.solve(); _writer.flush() }
fun PrintWriter.solve() {
//    val startTime = System.nanoTime()

    val numCases = 1//readInt()
    case@ for(case in 1..numCases) {
        //print("Case #$case: ")

        val n = readInt()
        val m = readInt()
        val s = readCharArray(n)

        val co = Array(m) { IntArray(m) }

        for(i in 1 until n) {
            val a = s[i-1] - 'a'
            val b = s[i] - 'a'
            co[a][b]++
            co[b][a]++
        }

        val M = 1 shl m
        val dp = IntArray(M) { inf }
        dp[0] = 0
        val cntBit = IntArray(M)
        for(msk in 1 until M) {
            cntBit[msk] = 1 + cntBit[msk and msk-1]
        }

        val d = Array(m) { IntArray(M) }
        for(msk in 1 until M) {
            val b = msk.countLeadingZeroBits()
            for(i in 0 until m) {
                d[i][msk] = d[i][msk.flipBit(b)] + co[i][b]
            }
        }

        val B = M-1

        for(msk in 0 until M) {
            for(i in 0 until m) {
                if(msk.containsBit(i)) continue
                val pos = cntBit[msk]
                val nmsk = msk or (1 shl i)
                dp.setMin(nmsk, dp[msk] + pos * (d[i][msk] - d[i][B xor nmsk]))
            }
        }

        val ans = dp[B]
        println(ans)
    }

//    iprintln("Time: ${(System.nanoTime() - startTime) / 1000000} ms")
}

const val inf = Int.MAX_VALUE
fun IntArray.setMin(i: Int, v: Int) { if(v < this[i]) this[i] = v }
inline fun Int.containsBit(x: Int) = this ushr x and 1 == 1
inline fun Int.flipBit(x: Int) = this xor (1 shl x)

/** IO */
//@JvmField val ONLINE_JUDGE = System.getProperty("ONLINE_JUDGE") != null
//const val PATH = "src/main/resources/"
//@JvmField val INPUT = File(PATH + "input.txt").inputStream()
//@JvmField val OUTPUT = File(PATH + "output.txt").outputStream()
@JvmField val INPUT = System.`in`
@JvmField val OUTPUT = System.out

const val _BUFFER_SIZE = 1 shl 16
@JvmField val _buffer = ByteArray(_BUFFER_SIZE)
@JvmField var _bufferPt = 0
@JvmField var _bytesRead = 0

tailrec fun readChar(): Char {
    if(_bufferPt == _bytesRead) {
        _bufferPt = 0
        _bytesRead = INPUT.read(_buffer, 0, _BUFFER_SIZE)
    }
    return if(_bytesRead < 0) Char.MIN_VALUE
    else {
        val c = _buffer[_bufferPt++].toChar()
        if (c == '\r') readChar() else c
    }
}

/** @param skipNext Whether to skip the next character (usually whitespace), defaults to true */
fun readCharArray(n: Int, skipNext: Boolean = true): CharArray {
    val res = CharArray(n) { readChar() }
    if(skipNext) readChar()
    return res
}

fun readLine(): String? {
    var c = readChar()
    return if(c == Char.MIN_VALUE) null
    else buildString {
        while(c != '\n' && c != Char.MIN_VALUE) {
            append(c)
            c = readChar()
        }
    }
}
fun readLn() = readLine()!!

fun read() = buildString {
    var c = readChar()
    while(c <= ' ') {
        if(c == Char.MIN_VALUE) return@buildString
        c = readChar()
    }
    do {
        append(c)
        c = readChar()
    } while(c > ' ')
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readLines(n: Int) = List(n) { readLn() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(OUTPUT, false)

/**  sort overrides to avoid quicksort attacks */

@JvmField var _random: Random? = null
val random get() = _random ?: Random(0x594E215C123 * System.nanoTime()).also { _random = it }

inline fun <A, T> _mergeSort(a0: A, n: Int, tmp0: A, get: A.(Int) -> T, set: A.(Int, T) -> Unit, cmp: (T, T) -> Int) {
    var a = a0
    var tmp = tmp0
    var len = 1
    while(len < n) {
        var l = 0
        while(true) {
            val m = l + len
            if(m >= n) break
            val r = min(n, m + len)
            var i = l
            var j = m
            for(k in l until r) {
                if(i != m && (j == r || cmp(a.get(i), a.get(j)) <= 0)) {
                    tmp.set(k, a.get(i++))
                } else tmp.set(k, a.get(j++))
            }
            l = r
        }
        for(i in l until n) tmp.set(i, a.get(i))
        val t = a; a = tmp; tmp = t
        len += len
    }
    if(a !== a0) for(i in 0 until n) a0.set(i, tmp0.get(i))
}

inline fun IntArray.sortWith(cmp: (Int, Int) -> Int) { _mergeSort(this, size, IntArray(size), IntArray::get, IntArray::set, cmp) }
inline fun <T: Comparable<T>> IntArray.sortBy(func: (Int) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> IntArray.sortByDescending(func: (Int) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun IntArray.sort() { sortBy { it } }
fun IntArray.sortDescending() { sortByDescending { it } }

inline fun LongArray.sortWith(cmp: (Long, Long) -> Int) { _mergeSort(this, size, LongArray(size), LongArray::get, LongArray::set, cmp) }
inline fun <T: Comparable<T>> LongArray.sortBy(func: (Long) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> LongArray.sortByDescending(func: (Long) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun LongArray.sort() { sortBy { it } }
fun LongArray.sortDescending() { sortByDescending { it } }

inline fun DoubleArray.sortWith(cmp: (Double, Double) -> Int) { _mergeSort(this, size, DoubleArray(size), DoubleArray::get, DoubleArray::set, cmp) }
inline fun <T: Comparable<T>> DoubleArray.sortBy(func: (Double) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> DoubleArray.sortByDescending(func: (Double) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun DoubleArray.sort() { sortBy { it } }
fun DoubleArray.sortDescending() { sortByDescending { it } }

inline fun CharArray.sort() { _sort() }
inline fun CharArray.sortDescending() { _sortDescending() }

inline fun <T : Comparable<T>> Array<out T>.sort() = _sort()
inline fun <T : Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
inline fun <T : Comparable<T>> MutableList<out T>.sort() = _sort()
inline fun <T : Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()

// import preserving junk function
@Suppress("NonAsciiCharacters") fun 雪花飄飄北風嘯嘯天地一片蒼茫() { iprintln(max(1, 2)) }

fun IntArray.sumLong() = sumOf { it.toLong() }

fun IntArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun IntArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun LongArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun LongArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun DoubleArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun DoubleArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> List<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> List<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }

// max/min Kotlin 1.6 -> 1.4 shim
fun IntArray.max() = maxOf { it }
fun IntArray.min() = minOf { it }
fun LongArray.max() = maxOf { it }
fun LongArray.min() = minOf { it }
fun CharArray.max() = maxOf { it }
fun CharArray.min() = minOf { it }
fun <T : Comparable<T>> Iterable<T>.max() = maxOf { it }
fun <T : Comparable<T>> Iterable<T>.min() = minOf { it }
fun <T : Comparable<T>> Sequence<T>.max() = maxOf { it }
fun <T : Comparable<T>> Sequence<T>.min() = minOf { it }


@file:Suppress("NOTHING_TO_INLINE", "EXPERIMENTAL_FEATURE_WARNING", "OVERRIDE_BY_INLINE")
@file:OptIn(ExperimentalStdlibApi::class)

import java.io.PrintWriter
import kotlin.math.*
import kotlin.random.Random
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending
import kotlin.io.println as iprintln

/** @author Spheniscine */
fun main() { _writer.solve(); _writer.flush() }
fun PrintWriter.solve() {
//    val startTime = System.nanoTime()

    val numCases = 1//readInt()
    case@ for(case in 1..numCases) {
        //print("Case #$case: ")

        val n = readInt()
        val m = readInt()
        val s = readCharArray(n)

        val co = Array(m) { IntArray(m) }

        for(i in 1 until n) {
            val a = s[i-1] - 'a'
            val b = s[i] - 'a'
            co[a][b]++
            co[b][a]++
        }

        val M = 1 shl m
        val dp = IntArray(M) { inf }
        dp[0] = 0
        val cntBit = IntArray(M)
        for(msk in 1 until M) {
            cntBit[msk] = 1 + cntBit[msk and msk-1]
        }

        val d = Array(m) { IntArray(M) }
        for(msk in 1 until M) {
            val b = msk.countTrailingZeroBits()
            for(i in 0 until m) {
                d[i][msk] = d[i][msk.flipBit(b)] + co[i][b]
            }
        }

        val B = M-1

        for(msk in 0 until M) {
            val pos = cntBit[msk]
            for(i in 0 until m) {
                if(msk.containsBit(i)) continue
                val nmsk = msk or (1 shl i)
                dp.setMin(nmsk, dp[msk] + pos * (d[i][msk] - d[i][B xor nmsk]))
            }
        }

        val ans = dp[B]
        println(ans)
    }

//    iprintln("Time: ${(System.nanoTime() - startTime) / 1000000} ms")
}

const val inf = Int.MAX_VALUE
fun IntArray.setMin(i: Int, v: Int) { if(v < this[i]) this[i] = v }
inline fun Int.containsBit(x: Int) = this ushr x and 1 == 1
inline fun Int.flipBit(x: Int) = this xor (1 shl x)

/** IO */
//@JvmField val ONLINE_JUDGE = System.getProperty("ONLINE_JUDGE") != null
//const val PATH = "src/main/resources/"
//@JvmField val INPUT = File(PATH + "input.txt").inputStream()
//@JvmField val OUTPUT = File(PATH + "output.txt").outputStream()
@JvmField val INPUT = System.`in`
@JvmField val OUTPUT = System.out

const val _BUFFER_SIZE = 1 shl 16
@JvmField val _buffer = ByteArray(_BUFFER_SIZE)
@JvmField var _bufferPt = 0
@JvmField var _bytesRead = 0

tailrec fun readChar(): Char {
    if(_bufferPt == _bytesRead) {
        _bufferPt = 0
        _bytesRead = INPUT.read(_buffer, 0, _BUFFER_SIZE)
    }
    return if(_bytesRead < 0) Char.MIN_VALUE
    else {
        val c = _buffer[_bufferPt++].toChar()
        if (c == '\r') readChar() else c
    }
}

/** @param skipNext Whether to skip the next character (usually whitespace), defaults to true */
fun readCharArray(n: Int, skipNext: Boolean = true): CharArray {
    val res = CharArray(n) { readChar() }
    if(skipNext) readChar()
    return res
}

fun readLine(): String? {
    var c = readChar()
    return if(c == Char.MIN_VALUE) null
    else buildString {
        while(c != '\n' && c != Char.MIN_VALUE) {
            append(c)
            c = readChar()
        }
    }
}
fun readLn() = readLine()!!

fun read() = buildString {
    var c = readChar()
    while(c <= ' ') {
        if(c == Char.MIN_VALUE) return@buildString
        c = readChar()
    }
    do {
        append(c)
        c = readChar()
    } while(c > ' ')
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readLines(n: Int) = List(n) { readLn() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(OUTPUT, false)

/**  sort overrides to avoid quicksort attacks */

@JvmField var _random: Random? = null
val random get() = _random ?: Random(0x594E215C123 * System.nanoTime()).also { _random = it }

inline fun <A, T> _mergeSort(a0: A, n: Int, tmp0: A, get: A.(Int) -> T, set: A.(Int, T) -> Unit, cmp: (T, T) -> Int) {
    var a = a0
    var tmp = tmp0
    var len = 1
    while(len < n) {
        var l = 0
        while(true) {
            val m = l + len
            if(m >= n) break
            val r = min(n, m + len)
            var i = l
            var j = m
            for(k in l until r) {
                if(i != m && (j == r || cmp(a.get(i), a.get(j)) <= 0)) {
                    tmp.set(k, a.get(i++))
                } else tmp.set(k, a.get(j++))
            }
            l = r
        }
        for(i in l until n) tmp.set(i, a.get(i))
        val t = a; a = tmp; tmp = t
        len += len
    }
    if(a !== a0) for(i in 0 until n) a0.set(i, tmp0.get(i))
}

inline fun IntArray.sortWith(cmp: (Int, Int) -> Int) { _mergeSort(this, size, IntArray(size), IntArray::get, IntArray::set, cmp) }
inline fun <T: Comparable<T>> IntArray.sortBy(func: (Int) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> IntArray.sortByDescending(func: (Int) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun IntArray.sort() { sortBy { it } }
fun IntArray.sortDescending() { sortByDescending { it } }

inline fun LongArray.sortWith(cmp: (Long, Long) -> Int) { _mergeSort(this, size, LongArray(size), LongArray::get, LongArray::set, cmp) }
inline fun <T: Comparable<T>> LongArray.sortBy(func: (Long) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> LongArray.sortByDescending(func: (Long) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun LongArray.sort() { sortBy { it } }
fun LongArray.sortDescending() { sortByDescending { it } }

inline fun DoubleArray.sortWith(cmp: (Double, Double) -> Int) { _mergeSort(this, size, DoubleArray(size), DoubleArray::get, DoubleArray::set, cmp) }
inline fun <T: Comparable<T>> DoubleArray.sortBy(func: (Double) -> T) { sortWith { a, b -> func(a).compareTo(func(b)) } }
inline fun <T: Comparable<T>> DoubleArray.sortByDescending(func: (Double) -> T) { sortWith { a, b -> func(b).compareTo(func(a)) } }
fun DoubleArray.sort() { sortBy { it } }
fun DoubleArray.sortDescending() { sortByDescending { it } }

inline fun CharArray.sort() { _sort() }
inline fun CharArray.sortDescending() { _sortDescending() }

inline fun <T : Comparable<T>> Array<out T>.sort() = _sort()
inline fun <T : Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
inline fun <T : Comparable<T>> MutableList<out T>.sort() = _sort()
inline fun <T : Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()

// import preserving junk function
@Suppress("NonAsciiCharacters") fun 雪花飄飄北風嘯嘯天地一片蒼茫() { iprintln(max(1, 2)) }

fun IntArray.sumLong() = sumOf { it.toLong() }

fun IntArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun IntArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun LongArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun LongArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun DoubleArray.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun DoubleArray.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> Array<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }
fun <T: Comparable<T>> List<T>.sortedIndices() = IntArray(size) { it }.also { it.sortBy(::get) }
fun <T: Comparable<T>> List<T>.sortedIndicesDescending() = IntArray(size) { it }.also { it.sortByDescending(::get) }

// max/min Kotlin 1.6 -> 1.4 shim
fun IntArray.max() = maxOf { it }
fun IntArray.min() = minOf { it }
fun LongArray.max() = maxOf { it }
fun LongArray.min() = minOf { it }
fun CharArray.max() = maxOf { it }
fun CharArray.min() = minOf { it }
fun <T : Comparable<T>> Iterable<T>.max() = maxOf { it }
fun <T : Comparable<T>> Iterable<T>.min() = minOf { it }
fun <T : Comparable<T>> Sequence<T>.max() = maxOf { it }
fun <T : Comparable<T>> Sequence<T>.min() = minOf { it }


@file:Suppress("NOTHING_TO_INLINE")

import java.io.PrintWriter
import java.util.StringTokenizer
import kotlin.math.*
import kotlin.random.*
import kotlin.system.*
import kotlin.collections.sort as _sort
import kotlin.collections.sortDescending as _sortDescending

fun main() {
    //output {
        val n = readInt()
        val m = readInt()
        val s = readLn()

        val co = Array(m) { IntArray(m) }

        for(i in 1 until n) {
            val a = s[i-1]
            val b = s[i]
            co[a - 'a'][b - 'a']++
            co[b - 'a'][a - 'a']++
        }

        val dp = IntArray(1 shl m) { inf }
        dp[0] = 0

        for(mask in 1 until dp.size) {
            val pos = mask.bitCount()
            for(x in 0 until m) {
                if(mask.containsBit(x)) {
                    var sum = dp[mask.flipBit(x)]
                    for(y in 0 until m) {
                        if(x == y) continue
                        val v = co[x][y] * pos
                        if(mask.containsBit(y)) sum += v else sum -= v
                    }
                    dp.setMin(mask, sum)
                }
            }
        }

        val ans = dp.last()

        println(ans)
    //}
}

const val inf = Int.MAX_VALUE
fun IntArray.setMin(i: Int, v: Int) { if(v < this[i]) this[i] = v }
inline fun Int.bitCount() = Integer.bitCount(this)
inline fun Int.containsBit(x: Int) = this ushr x and 1 == 1
inline fun Int.flipBit(x: Int) = this xor (1 shl x)

/** IO code start */
@JvmField val _reader = System.`in`.bufferedReader()
fun readLine(): String? = _reader.readLine()
fun readLn() = _reader.readLine()!!
@JvmField var _tokenizer: StringTokenizer = StringTokenizer("")
fun read(): String {
    while (_tokenizer.hasMoreTokens().not()) _tokenizer = StringTokenizer(_reader.readLine() ?: return "", " ")
    return _tokenizer.nextToken()
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readIntArray(n: Int) = IntArray(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readDoubleArray(n: Int) = DoubleArray(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }
fun readLongArray(n: Int) = LongArray(n) { read().toLong() }

@JvmField val _writer = PrintWriter(System.out, false)
inline fun output(block: PrintWriter.()->Unit) { _writer.apply(block).flush() }
inline fun iprintln(o: Any?) { println(o) } // immediate println for interactive, bypasses output{} blocks

/** shuffles and sort overrides to avoid quicksort attacks */
//private inline fun <R, V> R._shuffle(rnd: Random, get: R.(Int) -> V, set: R.(Int, V) -> Unit, size: Int) {
//    // Fisher-Yates shuffle algorithm
//    for (i in size - 1 downTo 1) {
//        val j = rnd.nextInt(i + 1)
//        val temp = get(i)
//        set(i, get(j))
//        set(j, temp)
//    }
//}
//
//@JvmField val random = Random(System.nanoTime())
//
//fun IntArray.shuffle(rnd: Random = random) = _shuffle(rnd, IntArray::get, IntArray::set, size)
//fun IntArray.sort() { shuffle(); _sort() }
//fun IntArray.sortDescending() { shuffle(); _sortDescending() }
//
//fun LongArray.shuffle(rnd: Random = random) = _shuffle(rnd, LongArray::get, LongArray::set, size)
//fun LongArray.sort() { shuffle(); _sort() }
//fun LongArray.sortDescending() { shuffle(); _sortDescending() }
//
//fun DoubleArray.shuffle(rnd: Random = random) = _shuffle(rnd, DoubleArray::get, DoubleArray::set, size)
//fun DoubleArray.sort() { shuffle(); _sort() }
//fun DoubleArray.sortDescending() { shuffle(); _sortDescending() }
//
//fun CharArray.shuffle(rnd: Random = random) = _shuffle(rnd, CharArray::get, CharArray::set, size)
//inline fun CharArray.sort() { _sort() }
//inline fun CharArray.sortDescending() { _sortDescending() }
//
//inline fun <T: Comparable<T>> Array<out T>.sort() = _sort()
//inline fun <T: Comparable<T>> Array<out T>.sortDescending() = _sortDescending()
//inline fun <T: Comparable<T>> MutableList<out T>.sort() = _sort()
//inline fun <T: Comparable<T>> MutableList<out T>.sortDescending() = _sortDescending()


import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*
import kotlin.math.min

fun main() {
    val jin = BufferedReader(InputStreamReader(System.`in`))
    var tokenizer = StringTokenizer(jin.readLine())
    val n = tokenizer.nextToken().toInt()
    val m = tokenizer.nextToken().toInt()
    val s = jin.readLine()
    val freq = Array(m) { IntArray(m) }
    for (j in 1 until n) {
        freq[s[j - 1] - 'a'][s[j] - 'a']++
        freq[s[j] - 'a'][s[j - 1] - 'a']++
    }
    val addend = IntArray(1 shl m) { -1 }
    val dp = IntArray(1 shl m) { Int.MAX_VALUE }
    addend[0] = 0
    dp[0] = 0
    for (mask in 1 until (1 shl m)) {
        for (e in 0 until m) {
            if ((mask shr e) and 1 != 0) {
                dp[mask] = min(dp[mask], dp[mask - (1 shl e)])
                if (addend[mask] == -1) {
                    addend[mask] = addend[mask - (1 shl e)]
                    for (f in 0 until m) {
                        if (f == e) {
                            Unit
                        } else if ((mask shr f) and 1 == 0) {
                            addend[mask] += freq[e][f]
                        } else {
                            addend[mask] -= freq[e][f]
                        }
                    }
                }
            }
        }
        dp[mask] += addend[mask]
    }
    println(dp[(1 shl m) - 1])
}


package E74;

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;

public class E {

	public static void main(String[] args) throws Exception {
		FastScanner sc = new FastScanner(System.in);
		FastPrinter out = new FastPrinter(System.out);
		E runner = new E();
		runner.run(sc, out);
		out.close();
	}

	int N, L;
	long[][] mat;
	long[] dp;

	public void run(FastScanner sc, FastPrinter out) throws Exception {
		L = sc.nextInt();
		N = sc.nextInt();
		mat = new long[N][N];
		char[] arr = sc.next().toCharArray();
		for (int i = 0; i < L - 1; i++) {
			int cur = arr[i] - 'a';
			int next = arr[i + 1] - 'a';
			if (cur != next) {
				mat[cur][next]++;
				mat[next][cur]++;
			}
		}
		dp = new long[1 << N];
		Arrays.fill(dp, -1);
		dp[(1 << N) - 1] = 0;
		long ans = solve(0);
		out.println(ans);
	}

	private long solve(int mask) {
		if (dp[mask] == -1) {
			long value = 0;
			for (int i = 0; i < N; i++) {
				if ((mask & (1 << i)) == 0) {
					for (int j = 0; j < N; j++) {
						if ((mask & (1 << j)) != 0) {
							value += mat[i][j];
						}
					}
				}
			}
			long ans = Long.MAX_VALUE;
			for (int i = 0; i < N; i++) {
				if ((mask & (1 << i)) == 0) {
					long temp = solve(mask | 1 << i);
					if (temp < ans) {
						ans = temp;
					}
				}
			}
			ans += value;
			dp[mask] = ans;
		}
		return dp[mask];
	}

	public void shuffle(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			int r = (int) (Math.random() * arr.length);
			if (i != r) {
				arr[i] ^= arr[r];
				arr[r] ^= arr[i];
				arr[i] ^= arr[r];
			}
		}
	}

	static class FastScanner {
		final private int BUFFER_SIZE = 1 << 10;
		private DataInputStream din;
		private byte[] buffer;
		private int bufferPointer, bytesRead;

		public FastScanner() {
			this(System.in);
		}

		public FastScanner(InputStream stream) {
			din = new DataInputStream(stream);
			buffer = new byte[BUFFER_SIZE];
			bufferPointer = bytesRead = 0;
		}

		public FastScanner(String fileName) throws IOException {
			Path p = Paths.get(fileName);
			buffer = Files.readAllBytes(p);
			bytesRead = buffer.length;
		}

		int[] nextIntArray(int N) throws IOException {
			int[] arr = new int[N];
			for (int i = 0; i < N; i++) {
				arr[i] = nextInt();
			}
			return arr;
		}

		String nextLine() throws IOException {
			int c = read();
			while (c != -1 && isEndline(c))
				c = read();
			if (c == -1) {
				return null;
			}
			StringBuilder res = new StringBuilder();
			do {
				if (c >= 0) {
					res.appendCodePoint(c);
				}
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		String next() throws Exception {
			int c = readOutSpaces();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public int nextInt() throws IOException {
			int ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (neg) return -ret;
			return ret;
		}

		public long nextLong() throws IOException {
			long ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');
			if (neg) return -ret;
			return ret;
		}

		public double nextDouble() throws IOException {
			double ret = 0, div = 1;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();

			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (c == '.') {
				while ((c = read()) >= '0' && c <= '9') {
					ret += (c - '0') / (div *= 10);
				}
			}

			if (neg) return -ret;
			return ret;
		}

		private void fillBuffer() throws IOException {
			if (din == null) {
				bufferPointer = 0;
				bytesRead = -1;
			} else {
				bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
			}
			if (bytesRead == -1) buffer[0] = -1;
		}

		private byte read() throws IOException {
			if (bufferPointer == bytesRead) fillBuffer();
			return buffer[bufferPointer++];
		}

		private int readOutSpaces() throws IOException {
			while (true) {
				if (bufferPointer == bytesRead) fillBuffer();
				int c = buffer[bufferPointer++];
				if (!isSpaceChar(c)) {
					return c;
				}
			}
		}

		public void close() throws IOException {
			if (din == null) return;
			din.close();
		}

		public int[][] readGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][] adj = new int[N][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][2];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				input[i][0] = a;
				input[i][1] = b;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				adj[a][numNodes[a]++] = b;
				if (bidirectional) adj[b][numNodes[b]++] = a;
			}
			return adj;
		}

		public int[][][] readWeightedGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][][] adj = new int[N][][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][3];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				int d = nextInt();
				input[i][0] = a;
				input[i][1] = b;
				input[i][2] = d;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]][2];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				int d = input[i][2];
				adj[a][numNodes[a]][0] = b;
				adj[a][numNodes[a]][1] = d;
				numNodes[a]++;
				if (bidirectional) {
					adj[b][numNodes[b]][0] = a;
					adj[b][numNodes[b]][1] = d;
					numNodes[b]++;
				}
			}
			return adj;
		}
	}

	static class FastPrinter {
		static final char ENDL = '\n';
		StringBuilder buf;
		PrintWriter pw;

		public FastPrinter(OutputStream stream) {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream)));
		}

		public FastPrinter(String fileName) throws Exception {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
		}

		public FastPrinter(StringBuilder buf) {
			this.buf = buf;
		}

		public void print(int a) {
			buf.append(a);
		}

		public void print(long a) {
			buf.append(a);
		}

		public void print(char a) {
			buf.append(a);
		}

		public void print(char[] a) {
			buf.append(a);
		}

		public void print(double a) {
			buf.append(a);
		}

		public void print(String a) {
			buf.append(a);
		}

		public void print(Object a) {
			buf.append(a.toString());
		}

		public void println() {
			buf.append(ENDL);
		}

		public void println(int a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(long a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char[] a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(double a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(String a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(Object a) {
			buf.append(a.toString());
			buf.append(ENDL);
		}

		public void printf(String format, Object... args) {
			buf.append(String.format(format, args));
		}

		public void close() {
			pw.print(buf);
			pw.close();
		}

		public void flush() {
			pw.print(buf);
			pw.flush();
			buf.setLength(0);
		}

	}

}


import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;

public class E {

	public static void main(String[] args) throws Exception {
		FastScanner sc = new FastScanner(System.in);
		FastPrinter out = new FastPrinter(System.out);
		E runner = new E();
		runner.run(sc, out);
		out.close();
	}

	long bestAns = 0;
	int[][] count;
	int N, L;
	long[][] mat;
	int[] pos;
	long[][][] stack;

	public void run(FastScanner sc, FastPrinter out) throws Exception {
		L = sc.nextInt();
		N = sc.nextInt();
		mat = new long[N][N];
		char[] arr = sc.next().toCharArray();
		count = new int[N][2];
		for (int i = 0; i < N; i++) {
			count[i][0] = i;
		}
		for (int i = 0; i < L - 1; i++) {
			int cur = arr[i] - 'a';
			int next = arr[i + 1] - 'a';
			if (cur != next) {
				mat[cur][next]++;
				mat[next][cur]++;
				count[cur][1]++;
				count[next][1]++;
			}
		}
		count[arr[L - 1] - 'a'][1]++;
		Arrays.sort(count, new Comparator<int[]>() {
			@Override
			public int compare(int[] a, int[] b) {
				return b[1] - a[1];
			}
		});
		// System.out.println(Arrays.deepToString(count));
		pos = new int[N];
		Arrays.fill(pos, -1);
		stack = new long[N][][];
		for (int i = 0; i < N; i++) {
			stack[i] = new long[N - i][2];
		}
		answer = Integer.MAX_VALUE;
		solve(0, 0, 0);
		System.out.println(answer);
	}

	Comparator<long[]> comp = new Comparator<long[]>() {

		@Override
		public int compare(long[] a, long[] b) {
			long diff = a[1] - b[1];
			if (diff == 0) {
				int x = (N / 2) - (int) a[0];
				int y = (N / 2) - (int) b[0];
				if (x < 0) x = -x;
				if (y < 0) y = -x;
				return y - x;
			} else if (diff < 0) {
				return -1;
			} else {
				return 1;
			}
		}
	};
	long answer;

	private void solve(int index, int mask, long curValue) {
		if (index == N) {
			if (curValue < answer) {
				answer = curValue;
			}
			return;
		} else {
			long retValue = curValue;
			if (retValue >= answer) {
				// System.out.println("useful");
				return;
			}
			for (int p = 0; p < N; p++) {
				if (pos[p] == -1) {
					long min = Long.MAX_VALUE;
					for (int i = 0; i < N; i++) {
						if ((mask & (1 << i)) == 0) {
							long value = computeTrans(p, i);
							if (value < min) {
								min = value;
							}
						}
					}
					retValue += min;
					for (int p2 = p + 1; p2 < N; p2++) {
						if (p2 == -1) {
							retValue += mat[p][p2];
						}
					}
				}
			}
			if (retValue >= answer) {
				// System.out.println("useful");
				return;
			}
		}
		int num = count[index][0];
		int c = 0;
		for (int i = 0; i < N; i++) {
			if ((mask & (1 << i)) == 0) {
				long value = computeTrans(num, i);
				stack[index][c][0] = i;
				stack[index][c++][1] = value;
			}
		}
		Arrays.sort(stack[index], comp);
		// System.out.println(Arrays.deepToString(stack[index]));
		for (int i = 0; i < c; i++) {
			int p = (int) stack[index][i][0];
			pos[num] = p;
			solve(index + 1, mask | (1 << p), curValue + stack[index][i][1]);
			pos[num] = -1;
		}
	}

	private long computeTrans(int num, int loc) {
		long value = 0;
		for (int i = 0; i < N; i++) {
			if (i == num || pos[i] == -1) continue;
			int diff = pos[i] - loc;
			if (diff < 0) diff = -diff;
			value += diff * mat[i][num];
		}
		return value;
	}

	public void shuffle(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			int r = (int) (Math.random() * arr.length);
			if (i != r) {
				arr[i] ^= arr[r];
				arr[r] ^= arr[i];
				arr[i] ^= arr[r];
			}
		}
	}

	static class FastScanner {
		final private int BUFFER_SIZE = 1 << 10;
		private DataInputStream din;
		private byte[] buffer;
		private int bufferPointer, bytesRead;

		public FastScanner() {
			this(System.in);
		}

		public FastScanner(InputStream stream) {
			din = new DataInputStream(stream);
			buffer = new byte[BUFFER_SIZE];
			bufferPointer = bytesRead = 0;
		}

		public FastScanner(String fileName) throws IOException {
			Path p = Paths.get(fileName);
			buffer = Files.readAllBytes(p);
			bytesRead = buffer.length;
		}

		int[] nextIntArray(int N) throws IOException {
			int[] arr = new int[N];
			for (int i = 0; i < N; i++) {
				arr[i] = nextInt();
			}
			return arr;
		}

		String nextLine() throws IOException {
			int c = read();
			while (c != -1 && isEndline(c))
				c = read();
			if (c == -1) {
				return null;
			}
			StringBuilder res = new StringBuilder();
			do {
				if (c >= 0) {
					res.appendCodePoint(c);
				}
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		String next() throws Exception {
			int c = readOutSpaces();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public int nextInt() throws IOException {
			int ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (neg) return -ret;
			return ret;
		}

		public long nextLong() throws IOException {
			long ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');
			if (neg) return -ret;
			return ret;
		}

		public double nextDouble() throws IOException {
			double ret = 0, div = 1;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();

			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (c == '.') {
				while ((c = read()) >= '0' && c <= '9') {
					ret += (c - '0') / (div *= 10);
				}
			}

			if (neg) return -ret;
			return ret;
		}

		private void fillBuffer() throws IOException {
			if (din == null) {
				bufferPointer = 0;
				bytesRead = -1;
			} else {
				bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
			}
			if (bytesRead == -1) buffer[0] = -1;
		}

		private byte read() throws IOException {
			if (bufferPointer == bytesRead) fillBuffer();
			return buffer[bufferPointer++];
		}

		private int readOutSpaces() throws IOException {
			while (true) {
				if (bufferPointer == bytesRead) fillBuffer();
				int c = buffer[bufferPointer++];
				if (!isSpaceChar(c)) {
					return c;
				}
			}
		}

		public void close() throws IOException {
			if (din == null) return;
			din.close();
		}

		public int[][] readGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][] adj = new int[N][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][2];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				input[i][0] = a;
				input[i][1] = b;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				adj[a][numNodes[a]++] = b;
				if (bidirectional) adj[b][numNodes[b]++] = a;
			}
			return adj;
		}

		public int[][][] readWeightedGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][][] adj = new int[N][][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][3];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				int d = nextInt();
				input[i][0] = a;
				input[i][1] = b;
				input[i][2] = d;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]][2];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				int d = input[i][2];
				adj[a][numNodes[a]][0] = b;
				adj[a][numNodes[a]][1] = d;
				numNodes[a]++;
				if (bidirectional) {
					adj[b][numNodes[b]][0] = a;
					adj[b][numNodes[b]][1] = d;
					numNodes[b]++;
				}
			}
			return adj;
		}
	}

	static class FastPrinter {
		static final char ENDL = '\n';
		StringBuilder buf;
		PrintWriter pw;

		public FastPrinter(OutputStream stream) {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream)));
		}

		public FastPrinter(String fileName) throws Exception {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
		}

		public FastPrinter(StringBuilder buf) {
			this.buf = buf;
		}

		public void print(int a) {
			buf.append(a);
		}

		public void print(long a) {
			buf.append(a);
		}

		public void print(char a) {
			buf.append(a);
		}

		public void print(char[] a) {
			buf.append(a);
		}

		public void print(double a) {
			buf.append(a);
		}

		public void print(String a) {
			buf.append(a);
		}

		public void print(Object a) {
			buf.append(a.toString());
		}

		public void println() {
			buf.append(ENDL);
		}

		public void println(int a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(long a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char[] a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(double a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(String a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(Object a) {
			buf.append(a.toString());
			buf.append(ENDL);
		}

		public void printf(String format, Object... args) {
			buf.append(String.format(format, args));
		}

		public void close() {
			pw.print(buf);
			pw.close();
		}

		public void flush() {
			pw.print(buf);
			pw.flush();
			buf.setLength(0);
		}

	}

}


import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

public class E {

	public static void main(String[] args) throws Exception {
		FastScanner sc = new FastScanner(System.in);
		FastPrinter out = new FastPrinter(System.out);
		E runner = new E();
		runner.run(sc, out);
		out.close();
	}

	int N, L;
	long[][] mat;
	long[] dp;

	public void run(FastScanner sc, FastPrinter out) throws Exception {
		L = sc.nextInt();
		N = sc.nextInt();
		mat = new long[N][N];
		char[] arr = sc.next().toCharArray();
		for (int i = 0; i < L - 1; i++) {
			int cur = arr[i] - 'a';
			int next = arr[i + 1] - 'a';
			if (cur != next) {
				mat[cur][next]++;
				mat[next][cur]++;
			}
		}
		dp = new long[1 << N];
		Arrays.fill(dp, -1);
		dp[(1 << N) - 1] = 0;
		long ans = solve(0);
		out.println(ans);
	}

	private long solve(int mask) {
		if (dp[mask] == -1) {
			long value = 0;
			for (int i = 0; i < N; i++) {
				if ((mask & (1 << i)) == 0) {
					for (int j = 0; j < N; j++) {
						if ((mask & (1 << j)) != 0) {
							value += mat[i][j];
						}
					}
				}
			}
			long ans = Long.MAX_VALUE;
			for (int i = 0; i < N; i++) {
				if ((mask & (1 << i)) == 0) {
					long temp = solve(mask | 1 << i);
					if (temp < ans) {
						ans = temp;
					}
				}
			}
			ans += value;
			dp[mask] = ans;
		}
		return dp[mask];
	}

	public void shuffle(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			int r = (int) (Math.random() * arr.length);
			if (i != r) {
				arr[i] ^= arr[r];
				arr[r] ^= arr[i];
				arr[i] ^= arr[r];
			}
		}
	}

	static class FastScanner {
		final private int BUFFER_SIZE = 1 << 10;
		private DataInputStream din;
		private byte[] buffer;
		private int bufferPointer, bytesRead;

		public FastScanner() {
			this(System.in);
		}

		public FastScanner(InputStream stream) {
			din = new DataInputStream(stream);
			buffer = new byte[BUFFER_SIZE];
			bufferPointer = bytesRead = 0;
		}

		public FastScanner(String fileName) throws IOException {
			Path p = Paths.get(fileName);
			buffer = Files.readAllBytes(p);
			bytesRead = buffer.length;
		}

		int[] nextIntArray(int N) throws IOException {
			int[] arr = new int[N];
			for (int i = 0; i < N; i++) {
				arr[i] = nextInt();
			}
			return arr;
		}

		String nextLine() throws IOException {
			int c = read();
			while (c != -1 && isEndline(c))
				c = read();
			if (c == -1) {
				return null;
			}
			StringBuilder res = new StringBuilder();
			do {
				if (c >= 0) {
					res.appendCodePoint(c);
				}
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		String next() throws Exception {
			int c = readOutSpaces();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public int nextInt() throws IOException {
			int ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (neg) return -ret;
			return ret;
		}

		public long nextLong() throws IOException {
			long ret = 0;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();
			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');
			if (neg) return -ret;
			return ret;
		}

		public double nextDouble() throws IOException {
			double ret = 0, div = 1;
			byte c = read();
			while (c <= ' ')
				c = read();
			boolean neg = (c == '-');
			if (neg) c = read();

			do {
				ret = ret * 10 + c - '0';
			} while ((c = read()) >= '0' && c <= '9');

			if (c == '.') {
				while ((c = read()) >= '0' && c <= '9') {
					ret += (c - '0') / (div *= 10);
				}
			}

			if (neg) return -ret;
			return ret;
		}

		private void fillBuffer() throws IOException {
			if (din == null) {
				bufferPointer = 0;
				bytesRead = -1;
			} else {
				bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
			}
			if (bytesRead == -1) buffer[0] = -1;
		}

		private byte read() throws IOException {
			if (bufferPointer == bytesRead) fillBuffer();
			return buffer[bufferPointer++];
		}

		private int readOutSpaces() throws IOException {
			while (true) {
				if (bufferPointer == bytesRead) fillBuffer();
				int c = buffer[bufferPointer++];
				if (!isSpaceChar(c)) {
					return c;
				}
			}
		}

		public void close() throws IOException {
			if (din == null) return;
			din.close();
		}

		public int[][] readGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][] adj = new int[N][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][2];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				input[i][0] = a;
				input[i][1] = b;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				adj[a][numNodes[a]++] = b;
				if (bidirectional) adj[b][numNodes[b]++] = a;
			}
			return adj;
		}

		public int[][][] readWeightedGraph(int N, int M, boolean zeroIndexed, boolean bidirectional) throws Exception {
			int[][][] adj = new int[N][][];
			int[] numNodes = new int[N];
			int[][] input = new int[M][3];
			for (int i = 0; i < M; i++) {
				int a = nextInt();
				int b = nextInt();
				if (zeroIndexed) {
					a--;
					b--;
				}
				int d = nextInt();
				input[i][0] = a;
				input[i][1] = b;
				input[i][2] = d;
				numNodes[a]++;
				if (bidirectional) numNodes[b]++;
			}
			for (int i = 0; i < N; i++) {
				adj[i] = new int[numNodes[i]][2];
				numNodes[i] = 0;
			}
			for (int i = 0; i < M; i++) {
				int a = input[i][0];
				int b = input[i][1];
				int d = input[i][2];
				adj[a][numNodes[a]][0] = b;
				adj[a][numNodes[a]][1] = d;
				numNodes[a]++;
				if (bidirectional) {
					adj[b][numNodes[b]][0] = a;
					adj[b][numNodes[b]][1] = d;
					numNodes[b]++;
				}
			}
			return adj;
		}
	}

	static class FastPrinter {
		static final char ENDL = '\n';
		StringBuilder buf;
		PrintWriter pw;

		public FastPrinter(OutputStream stream) {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream)));
		}

		public FastPrinter(String fileName) throws Exception {
			buf = new StringBuilder();
			pw = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
		}

		public FastPrinter(StringBuilder buf) {
			this.buf = buf;
		}

		public void print(int a) {
			buf.append(a);
		}

		public void print(long a) {
			buf.append(a);
		}

		public void print(char a) {
			buf.append(a);
		}

		public void print(char[] a) {
			buf.append(a);
		}

		public void print(double a) {
			buf.append(a);
		}

		public void print(String a) {
			buf.append(a);
		}

		public void print(Object a) {
			buf.append(a.toString());
		}

		public void println() {
			buf.append(ENDL);
		}

		public void println(int a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(long a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(char[] a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(double a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(String a) {
			buf.append(a);
			buf.append(ENDL);
		}

		public void println(Object a) {
			buf.append(a.toString());
			buf.append(ENDL);
		}

		public void printf(String format, Object... args) {
			buf.append(String.format(format, args));
		}

		public void close() {
			pw.print(buf);
			pw.close();
		}

		public void flush() {
			pw.print(buf);
			pw.flush();
			buf.setLength(0);
		}

	}

}


import java.util.*;
import java.io.*;

public class E {
    public static void main(String[] args) {
        FastScanner scanner = new FastScanner();
        PrintWriter out = new PrintWriter(System.out, false);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        char[] str = scanner.next().toCharArray();
        int maxMask = 1 << m;
        long[] dp = new long[maxMask];
        int[][] dists = new int[m][m];
        for(int i = 1; i < n; i++) {
            int c1 = str[i] - 'a';
            int c2 = str[i-1] - 'a';
            dists[c1][c2]++;
            dists[c2][c1]++;
        }
        int[] pre = new int[maxMask];
        for(int mask = 0; mask < maxMask; mask++) {
            for(int i = 0; i < m; i++) {
                if (((1 << i) & mask) == 0) continue;
                for(int j = 0; j < m; j++) {
                    if (((1 << j) & mask) > 0) continue;
                    pre[mask] += dists[i][j];
                }
            }
        }
        Arrays.fill(dp, Long.MAX_VALUE/4);
        dp[0] = 0;
        for(int mask = 0; mask < maxMask; mask++) {
            if (dp[mask] == Long.MAX_VALUE/4) continue;
            for(int i = 0; i < m; i++) {
                if (((1 << i) & mask) > 0) continue;
                int nmask = mask | (1 << i);
                dp[nmask] = Math.min(dp[nmask], dp[mask] + pre[nmask]);
            }
        }
        out.println(dp[maxMask - 1]);
        out.flush();
    }

    public static class FastScanner {
        BufferedReader br;
        StringTokenizer st;

        public FastScanner(Reader in) {
            br = new BufferedReader(in);
        }

        public FastScanner() {
            this(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

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

        long nextLong() {
            return Long.parseLong(next());
        }

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

        String readNextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}


import java.util.Arrays;
import java.util.Scanner;

public class thing {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);

		int n = in.nextInt();
		int m = in.nextInt();

		String s = in.next();

		int[][] count = new int[m][m];
		int[] dp = new int[1 << m];

		Arrays.fill(dp, Integer.MAX_VALUE);
		dp[0] = 0;

		for(int i = 1; i < n; i++) {
			int a = s.charAt(i)-'a';
			int b = s.charAt(i-1)-'a';
			count[a][b]++;
			count[b][a]++;
		}

		for(int i = 1; i < (1 << m); i++) {

			int pos = set_bits(i);

			for(int j = 0; (i >> j) != 0; j++) {

				if(((i >> j) & 1) == 0) continue;

				int sum = 0;

				for(int mask = i, y = 0; y < m; mask >>= 1, y++) {
					if(y == j) continue;
					if((mask & 1) == 1) sum += count[j][y];
					else sum -= count[j][y];
				}

				int calc = dp[i-(1<<j)] + pos*sum;

				dp[i] = Math.min(dp[i], calc);
			}

		}

		System.out.println(dp[(1 << m)-1]);

	}

	public static int set_bits(int n) {
		int count = 0;
		while (n > 0) {
			count += n & 1;
			n >>= 1;
		}
		return count;
	}

}


// practice with rainboy
import java.io.*;
import java.util.*;

public class CF1238E extends PrintWriter {
	CF1238E() { super(System.out, true); }
	Scanner sc = new Scanner(System.in);
	public static void main(String[] $) {
		CF1238E o = new CF1238E(); o.main(); o.flush();
	}

	static final int INF = 0x3f3f3f3f;
	void main() {
		int n = sc.nextInt();
		int m = sc.nextInt();
		byte[] cc = sc.next().getBytes();
		int[] kk = new int[1 << m];
		for (int i = 1; i < n; i++) {
			int a = cc[i - 1] - 'a';
			int b = cc[i] - 'a';
			kk[1 << a | 1 << b]++;
		}
		for (int h = 0; h < m; h++)
			for (int b = 0; b < 1 << m; b++)
				if ((b & 1 << h) == 0)
					kk[b | 1 << h] += kk[b];
		int[] dp = new int[1 << m];
		int m_ = (1 << m) - 1;
		for (int b = 1; b < 1 << m; b++) {
			int k = n - 1 - kk[b] - kk[m_ ^ b];
			int x = INF;
			for (int h = 0; h < m; h++)
				if ((b & 1 << h) != 0) {
					int b_ = b ^ 1 << h;
					x = Math.min(x, dp[b_]);
				}
			dp[b] = x == INF ? INF : x + k;
		}
		println(dp[m_]);
	}
}


import java.io.*;
import java.util.*;

public class cfe {
	static DataReader input;
	static int LARGE_INT = 1000000007;
	//static int MAXN = 300000;
	//static int[] len = new int[300000];
	//static int[] price = new int[300000];
	static final int[] BM =  {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
	                          1<<17, 1<<18, 1<<19, 1<<20};

	public static void main(String[] args) throws IOException {
	    input = new DataReader();
		PrintWriter out = new PrintWriter(System.out);
		int N = nextInt(), M = nextInt(), P = 1<<M;
		String x = nextStr();
		int c1 = x.charAt(0) - 'a';
		int[][] pair_count = new int[M][M];
		for (int i=1; i<N; i++) {
			int c2 = x.charAt(i) - 'a';
			if (c1 < c2)
				pair_count[c1][c2]++;
			else if (c1 > c2)
				pair_count[c2][c1]++;
			c1 = c2;
		}
		int[] group_count = new int[P];
		for (int mask = 1; mask <P; mask++) {
			int j;
			for (j=0; j<M; j++) {
				if ((mask & BM[j]) > 0)  break;
			}
			int nmask = mask ^ BM[j];
			int val = group_count[nmask];
			for (int i=0; i<j; i++) {
				if ((mask & BM[i]) > 0)  val -= pair_count[i][j];
				else                   val += pair_count[i][j];
			}
			for (int i=j+1; i<M; i++) {
				if ((mask & BM[i]) > 0)  val -= pair_count[j][i];
				else                   val += pair_count[j][i];
			}
			group_count[mask] = val;
		}
		// dp[k][mask] = Accumulated cost of first K positions filled, mask position 1 letters are used
		// dp[0][0] = 0;
		int[][] dp = new int[M+1][P];	   //
		for (int mask=1; mask<P; mask++) {  // each bit mask
			dp[0][mask] = 0;
			int k = Integer.bitCount(mask);  // computing first k position filled.
			int val = LARGE_INT;
			for (int j=0; j<M; j++) {
				if ((mask & BM[j]) > 0) {
					int nmask = mask ^ BM[j];
					val = Math.min(val, dp[k-1][nmask] + group_count[nmask]);
				}
			}
			dp[k][mask] = val;
		}
		//out.println(Arrays.deepToString(dp));
		out.println(dp[M][P-1]);
		out.flush();
		out.close();
	}
	static int nextInt() throws IOException {
        return Integer.parseInt(input.next());
    }
	static String nextStr() throws IOException {
        return input.next();
    }
    static class DataReader {
        BufferedReader br;
        StringTokenizer st;
        public DataReader() throws IOException {	// reading from standard in
			br = new BufferedReader(new InputStreamReader(System.in));
        }
		boolean hasNext() throws IOException {
            while (st == null || !st.hasMoreElements()) {
				String line = br.readLine();
				if (line == null) { // end of file
					return false;
				}
                st = new StringTokenizer(line);
            }
			return true;
		}
        String next() throws IOException {
			if (hasNext())
				return st.nextToken();
			else
				return null;
        }
    }
}


/**
 * BaZ :D
 */
import java.util.*;
import java.io.*;
import static java.lang.Math.*;

public class Main
{
    static MyScanner scan;
    static PrintWriter pw;
    static long MOD = 1_000_000_007;
    static long INF = 1_000_000_000_000_000_000L;
    static long inf = 2_000_000_000;
    public static void main(String[] args) {
        new Thread(null, null, "BaZ", 1 << 27) {
            public void run() {
                try {
                    solve();
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }.start();
    }
    static int n,m;
    static int dp[], cnt[][], sum[];
    static void solve() throws IOException
    {
        //initIo(true);
        initIo(false);
        StringBuilder sb = new StringBuilder();
        n = ni();
        m = ni();
        dp = new int[(1<<m)];
        char c[] = ne().toCharArray();
        cnt = new int[m][m];
        for(int i=0;i+1<n;++i) {
            if(c[i]!=c[i+1]) {
                ++cnt[c[i] - 'a'][c[i+1] - 'a'];
                ++cnt[c[i+1] - 'a'][c[i] - 'a'];
            }
        }
        sum = new int[1<<m];
        for(int mask=0;mask<(1<<m);++mask) {
            for(int i=0;i<m;++i) {
                if((mask&(1<<i))!=0) {
                    for(int j=0;j<m;++j) {
                        if((mask&(1<<j))==0) {
                            sum[mask]+=cnt[i][j];
                        }
                    }
                }
            }
        }
        Arrays.fill(dp, -1);
        pl(f(0));
        pw.flush();
        pw.close();
    }
    static int f(int mask) {
        if(mask==(1<<m) - 1) {
            return 0;
        }
        if(dp[mask]!=-1) {
            return dp[mask];
        }
        int min = Integer.MAX_VALUE;
        for(int i=0;i<m;++i) {
            if((mask&(1<<i))==0) {
                min  =  min(min, sum[mask]+f(mask|(1<<i)));
            }
        }
        return min;
    }
    static void initIo(boolean isFileIO) throws IOException {
        scan = new MyScanner(isFileIO);
        if(isFileIO) {
            pw = new PrintWriter("/Users/amandeep/Desktop/output.txt");
        }
        else {
            pw = new PrintWriter(System.out, true);
        }
    }
    static int ni() throws IOException
    {
        return scan.nextInt();
    }
    static long nl() throws IOException
    {
        return scan.nextLong();
    }
    static double nd() throws IOException
    {
        return scan.nextDouble();
    }
    static String ne() throws IOException
    {
        return scan.next();
    }
    static String nel() throws IOException
    {
        return scan.nextLine();
    }
    static void pl()
    {
        pw.println();
    }
    static void p(Object o)
    {
        pw.print(o+" ");
    }
    static void pl(Object o)
    {
        pw.println(o);
    }
    static void psb(StringBuilder sb)
    {
        pw.print(sb);
    }
    static void pa(String arrayName, Object arr[])
    {
        pl(arrayName+" : ");
        for(Object o : arr)
            p(o);
        pl();
    }
    static void pa(String arrayName, int arr[])
    {
        pl(arrayName+" : ");
        for(int o : arr)
            p(o);
        pl();
    }
    static void pa(String arrayName, long arr[])
    {
        pl(arrayName+" : ");
        for(long o : arr)
            p(o);
        pl();
    }
    static void pa(String arrayName, double arr[])
    {
        pl(arrayName+" : ");
        for(double o : arr)
            p(o);
        pl();
    }
    static void pa(String arrayName, char arr[])
    {
        pl(arrayName+" : ");
        for(char o : arr)
            p(o);
        pl();
    }
    static void pa(String listName, List list)
    {
        pl(listName+" : ");
        for(Object o : list)
            p(o);
        pl();
    }
    static void pa(String arrayName, Object[][] arr) {
        pl(arrayName+" : ");
        for(int i=0;i<arr.length;++i) {
            for(Object o : arr[i])
                p(o);
            pl();
        }
    }
    static void pa(String arrayName, int[][] arr) {
        pl(arrayName+" : ");
        for(int i=0;i<arr.length;++i) {
            for(int o : arr[i])
                p(o);
            pl();
        }
    }
    static void pa(String arrayName, long[][] arr) {
        pl(arrayName+" : ");
        for(int i=0;i<arr.length;++i) {
            for(long o : arr[i])
                p(o);
            pl();
        }
    }
    static void pa(String arrayName, char[][] arr) {
        pl(arrayName+" : ");
        for(int i=0;i<arr.length;++i) {
            for(char o : arr[i])
                p(o);
            pl();
        }
    }
    static void pa(String arrayName, double[][] arr) {
        pl(arrayName+" : ");
        for(int i=0;i<arr.length;++i) {
            for(double o : arr[i])
                p(o);
            pl();
        }
    }
    static class MyScanner
    {
        BufferedReader br;
        StringTokenizer st;
        MyScanner(boolean readingFromFile) throws IOException
        {
            if(readingFromFile) {
                br = new BufferedReader(new FileReader("/Users/amandeep/Desktop/input.txt"));
            }
            else {
                br = new BufferedReader(new InputStreamReader(System.in));
            }
        }
        String nextLine()throws IOException
        {
            return br.readLine();
        }
        String next() throws IOException
        {
            if(st==null || !st.hasMoreTokens())
                st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }
        int nextInt() throws IOException
        {
            return Integer.parseInt(next());
        }
        long nextLong() throws IOException
        {
            return Long.parseLong(next());
        }
        double nextDouble() throws IOException
        {
            return Double.parseDouble(next());
        }
    }
}