One unknown hacker wants to get the admin's password of AtForces testing system, to get problems from the next contest. To achieve that, he sneaked into the administrator's office and stole a piece of paper with a list of $n$ passwords — strings, consists of small Latin letters.

Hacker went home and started preparing to hack AtForces. He found that the system contains only passwords from the stolen list and that the system determines the equivalence of the passwords $a$ and $b$ as follows:  two passwords $a$ and $b$ are equivalent if there is a letter, that exists in both $a$ and $b$;  two passwords $a$ and $b$ are equivalent if there is a password $c$ from the list, which is equivalent to both $a$ and $b$. 

If a password is set in the system and an equivalent one is applied to access the system, then the user is accessed into the system.

For example, if the list contain passwords "a", "b", "ab", "d", then passwords "a", "b", "ab" are equivalent to each other, but the password "d" is not equivalent to any other password from list. In other words, if:  admin's password is "b", then you can access to system by using any of this passwords: "a", "b", "ab";  admin's password is "d", then you can access to system by using only "d". 

Only one password from the list is the admin's password from the testing system. Help hacker to calculate the minimal number of passwords, required to guaranteed access to the system. Keep in mind that the hacker does not know which password is set in the system.


-----Input-----

The first line contain integer $n$ ($1 \le n \le 2 \cdot 10^5$) — number of passwords in the list. Next $n$ lines contains passwords from the list – non-empty strings $s_i$, with length at most $50$ letters. Some of the passwords may be equal.

It is guaranteed that the total length of all passwords does not exceed $10^6$ letters. All of them consist only of lowercase Latin letters.


-----Output-----

In a single line print the minimal number of passwords, the use of which will allow guaranteed to access the system.


-----Examples-----
Input
4
a
b
ab
d

Output
2
Input
3
ab
bc
abc

Output
1
Input
1
codeforces

Output
1


-----Note-----

In the second example hacker need to use any of the passwords to access the system.

Difficulty: MEDIUM
Tags: Spanning trees

Graph algorithms

Graph traversal

Starter code:

Solutions:
X = int(input())
Z = set()
for I in range(X):
	Z.add(''.join(set(input())))
Z = list(Z)
N = len(Z)
C = 0
for I in range(N):
	for J in range(N):
		if I != J:
			if len(set(Z[I]).intersection(set(Z[J]))) > 0:
				C += 1
				break
if C == 0:
	if N > 26:
		print(26)
	else:
		print(N - C)
elif N - C + 1 > 26:
	print(26)
else:
	print(N - C + 1)


from collections import deque
import sys

def kek():
	n = int(input())
	graph = []
	for i in range(26):
		graph.append([False] * 28)
		graph[i][-1] = 0
	for i in range(n):
		s = sys.stdin.readline()
		for i in range(len(s) - 1):
			graph[ord(s[i]) - 97][-1] += 1
			for j in range(len(s) - 1):
				graph[ord(s[i]) - 97][ord(s[j]) - 97] = True
	used = [0] * 26
	ans = 0
	while True:
		queue = deque()
		for i in range(len(graph)):
			if graph[i][-1] != 0 and used[i] == 0:
				queue.append(i)
				ans += 1
				used[i] = 1
				break
		if not len(queue):
			print(ans)
			break
		while len(queue):
			pos = queue.pop()
			for i in range(len(graph[pos]) - 1):
				if graph[pos][i] == True and used[i] == 0:
					used[i] = 1
					queue.append(i)
kek()


import sys
input = sys.stdin.readline
import collections
from collections import defaultdict

def root(k):
	while k != l[k]:
		k = l[l[k]]
	return k
t = int(input())
l = {}
sz = {}
for i in range(t):
	m = input().rstrip()
	for j in m:
		if j not in l:
			l[j] = j
			sz[j] = 0
	maxsize = -1
	char = 0
	for j in m:
		if maxsize < sz[str(root(j))]:
			maxsize = sz[root(j)]
			char = root(j)
		if maxsize == sz[str(root(j))]:
			if root(j) < char:
				char = root(j)
	for i in m:
		if sz[root(i)] > 0 and i != char:
			sz[root(i)] = 0
		l[i] = char
	sz[char] += 1
	count = 0
	for i in sz:
		if sz[i] > 0:
			count += 1
print(count)


import sys
input = sys.stdin.readline

class DSU:

	def __init__(self, n):
		self.parent = list(range(n))
		self.rank = [0] * n

	def find(self, x):
		if self.parent[x] != x:
			self.parent[x] = self.find(self.parent[x])
		return self.parent[x]

	def union(self, x, y):
		(xr, yr) = (self.find(x), self.find(y))
		if xr == yr:
			return
		if self.rank[xr] < self.rank[yr]:
			self.parent[xr] = yr
		else:
			if self.rank[xr] == self.rank[yr]:
				self.rank[xr] += 1
			self.parent[yr] = xr
dsu = DSU(26)
f = set()
n = int(input())
for i in range(n):
	s = list(set((ord(i) - ord('a') for i in input().strip())))
	s.sort()
	p = dsu.find(s[0])
	for i in s:
		f.add(i)
		dsu.union(p, i)
f = list(f)
ans = 0
for i in f:
	if dsu.parent[i] == i:
		ans += 1
print(ans)


import string

class FindUnion(object):

	def __init__(self):
		self.sets = {}

	def makeset(self, x):
		self.sets.setdefault(x, x)

	def find(self, x):
		parent = self.sets[x]
		if self.sets[parent] == parent:
			return parent
		self.sets[x] = self.sets[parent]
		_ = self.find(x)
		return self.find(parent)

	def union(self, x, y):
		x_root = self.find(x)
		y_root = self.find(y)
		self.sets[x_root] = y_root

	def setcount(self):
		return sum((1 for (k, v) in self.sets.items() if k == v))
n = int(input())
F = {frozenset(input()) for _ in range(n)}
FU = FindUnion()
for f in F:
	letters = list(f)
	for a in letters:
		FU.makeset(a)
	for i in range(len(letters)):
		a = letters[i]
		for j in range(i + 1, len(letters)):
			b = letters[j]
			FU.union(a, b)
print(FU.setcount())


import sys
from math import sqrt, gcd, ceil, log
from collections import defaultdict, Counter, deque
inp = sys.stdin.readline
read = lambda : list(map(int, inp().strip().split()))

def main():

	def find(x):
		if not x in par:
			par[x] = x
			return x
		while par[x] != x:
			par[x] = par[par[x]]
			x = par[x]
		return x
	par = defaultdict(str)
	for _ in range(int(input())):
		s = input()
		p = find(s[0])
		for j in s[1:]:
			par[find(j)] = p
	sett = set()
	for i in par:
		sett.add(find(i))
	print(len(sett))
main()


from __future__ import division, print_function
from fractions import Fraction
import sys
import os
from io import BytesIO, IOBase
import bisect
from heapq import *
from math import *
from collections import deque
from collections import Counter as counter
from itertools import combinations as comb
from itertools import permutations as permutate
from bisect import bisect_left as bl
from bisect import bisect_right as br
from bisect import bisect
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')

def print(*args, **kwargs):
	(sep, file) = (kwargs.pop('sep', ' '), kwargs.pop('file', sys.stdout))
	at_start = True
	for x in args:
		if not at_start:
			file.write(sep)
		file.write(str(x))
		at_start = False
	file.write(kwargs.pop('end', '\n'))
	if kwargs.pop('flush', False):
		file.flush()
if sys.version_info[0] < 3:
	(sys.stdin, sys.stdout) = (FastIO(sys.stdin), FastIO(sys.stdout))
else:
	(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
mod = 1000000007

def inp():
	return sys.stdin.readline().rstrip('\r\n')

def out(var):
	sys.stdout.write(str(var))

def lis():
	return list(map(int, inp().split()))

def stringlis():
	return list(map(str, inp().split()))

def sep():
	return map(int, inp().split())

def strsep():
	return map(str, inp().split())

def zerolist(n):
	return [0] * n

def nextline():
	out('\n')

def testcase(t):
	for p in range(t):
		solve()

def printlist(a):
	for p in range(0, len(a)):
		out(str(a[p]) + ' ')

def lcm(a, b):
	return a * b // gcd(a, b)

def power(a, b):
	ans = 1
	while b > 0:
		if b % 2 == 1:
			ans *= a
		a *= a
		b //= 2
	return ans

def ncr(n, r):
	return factorial(n) // (factorial(r) * factorial(max(n - r, 1)))

def isPrime(n):
	if n <= 1:
		return False
	if n <= 3:
		return True
	if n % 2 == 0 or n % 3 == 0:
		return False
	i = 5
	while i * i <= n:
		if n % i == 0 or n % (i + 2) == 0:
			return False
		i = i + 6
	return True

def bs(a, l, h, x):
	while l < h:
		mid = (l + h) // 2
		if a[mid] == x:
			return mid
		if a[mid] < x:
			l = mid + 1
		else:
			h = mid
	return l

def sieve(a):
	for i in range(3, 10 ** 6 + 1, 2):
		a[i] = 1
	for i in range(3, 10 ** 6 + 1, 2):
		for j in range(i * i, 10 ** 6 + 1, i):
			a[j] = 0
	a[2] = 1
	return a

def bfs(g, st):
	visited = [-1] * len(g)
	visited[st] = 0
	queue = []
	queue.append(st)
	new = []
	while len(queue) != 0:
		s = queue.pop()
		new.append(s)
		for i in g[s]:
			if visited[i] == -1:
				visited[i] = visited[s] + 1
				queue.append(i)
	return visited

def dfsusingstack(v, st):
	d = deque([])
	visited = [0] * len(v)
	d.append(st)
	new = []
	visited[st] = 1
	while len(d) != 0:
		curr = d.pop()
		new.append(curr)
		for i in v[curr]:
			if visited[i] == 0:
				visited[i] = 1
				d.append(i)
	return new

def solve():
	n = int(inp())
	s = []
	graph = [[] for i in range(26)]
	have = [False] * 26
	ans = [0] * 26
	curr = 1
	notcount = {-1}
	for i in range(0, n):
		temp = list(inp())
		s.append(temp)
		temp = list(set(temp))
		lol = 10 ** 9
		for j in temp:
			if have[ord(j) - 97]:
				lol = min(lol, ans[ord(j) - 97])
			have[ord(j) - 97] = True
		if lol == 10 ** 9:
			for j in temp:
				ans[ord(j) - 97] = curr
			curr += 1
		else:
			for j in temp:
				ans[ord(j) - 97] = lol
	for i in range(0, n):
		lol = 10 ** 9
		for j in s[i]:
			lol = min(lol, ans[ord(j) - 97])
		for j in s[i]:
			ans[ord(j) - 97] = lol
	if 0 in ans:
		print(len(set(ans)) - 1)
		return
	print(len(set(ans)))
testcase(1)


import sys
input = sys.stdin.readline

def r1():
	return int(input())

def r2():
	return list(map(int, input().split()))
pred = {}
size = {}
k = 0

def get_lead(index):
	if pred[index] == index:
		return index
	pred[index] = get_lead(pred[index])
	return pred[index]

def merge(a, b, k):
	pred_a = get_lead(a)
	pred_b = get_lead(b)
	if pred_a == pred_b:
		return k
	k -= 1
	if size[pred_a] > size[pred_b]:
		(pred_a, pred_b) = (pred_b, pred_a)
	pred[pred_a] = pred_b
	size[pred_b] = max(size[pred_b], size[pred_a] + 1)
	return k
n = r1()
for i in range(n):
	psw = input()
	first = psw[0]
	for item in psw:
		if item == '\n':
			break
		if item not in pred:
			pred[item] = item
			size[item] = 1
			k += 1
		k = merge(item, first, k)
print(k)


n = int(input())
graph = {}
for i in range(n):
	s = input()
	base = set([c for c in s])
	for c in s:
		graph.setdefault(c, set()).update(base)
visited = set()

def dfs(i):
	visited.add(i)
	for j in graph[i]:
		if j not in visited:
			dfs(j)
count = 0
for j in graph:
	if j not in visited:
		count += 1
		dfs(j)
print(count)


import sys
input = sys.stdin.readline
n = int(input())
p = [i for i in range(26)]
r = [0 for i in range(26)]
used = dict()
k = 0

def get(x):
	while p[x] != x:
		x = p[x]
	return x

def union(x, y):
	x = get(x)
	y = get(y)
	if x == y:
		return 0
	if r[x] > r[y]:
		(x, y) = (y, x)
	p[x] = y
	if r[x] == r[y]:
		r[y] += 1
	return 1
for i in range(1, n + 1):
	s = input().rstrip()
	for c in s:
		if c == '\n':
			continue
		if ord(c) - 97 not in used:
			used[ord(c) - 97] = ord(c) - 97
		union(ord(c) - 97, ord(s[0]) - 97)
res = set()
for i in used:
	res.add(get(i))
print(len(res))


import sys

def find_parent(x):
	if x == parent[x]:
		return x
	parent[x] = find_parent(parent[x])
	return parent[x]

def union_sets(x, y):
	p_x = find_parent(x)
	p_y = find_parent(y)
	if p_x != p_y:
		if sz[p_x] >= sz[p_y]:
			parent[p_y] = p_x
			sz[p_x] += sz[p_y]
		else:
			parent[p_x] = p_y
			sz[p_y] += sz[p_x]
n = int(input())
parent = [i for i in range(26)]
sz = [0 for i in range(26)]
dicio = {}
cont = 0
for i in 'abcdefghijklmnopqrstuvwxyz':
	dicio[i] = cont
	cont += 1
for j in range(n):
	chave = input()
	tamanho = len(chave)
	chave = sorted(chave)
	for i in range(tamanho - 1):
		letra1 = dicio[chave[i]]
		letra2 = dicio[chave[i + 1]]
		if chave[i] == chave[i + 1] or parent[letra1] == parent[letra2]:
			continue
		union_sets(letra1, letra2)
	sz[find_parent(ord(chave[0]) - ord('a'))] += 1
ans = 0
for i in range(26):
	if sz[i] != 0 and find_parent(i) == i:
		ans += 1
print(ans)


class UnionFind(object):
	__slots__ = ['nodes']

	def __init__(self, n: int):
		self.nodes = [-1] * n

	def find(self, x: int) -> int:
		if self.nodes[x] < 0:
			return x
		else:
			self.nodes[x] = self.find(self.nodes[x])
			return self.nodes[x]

	def unite(self, x: int, y: int) -> bool:
		(root_x, root_y, nodes) = (self.find(x), self.find(y), self.nodes)
		if root_x != root_y:
			if nodes[root_x] > nodes[root_y]:
				(root_x, root_y) = (root_y, root_x)
			nodes[root_x] += nodes[root_y]
			nodes[root_y] = root_x
		return root_x != root_y
import sys
n = int(input())
uf = UnionFind(26 + n)
cc_a = ord('a')
for (i, p) in enumerate((l.rstrip() for l in sys.stdin), start=26):
	for cc in list(map(lambda c: ord(c) - cc_a, set(p))):
		uf.unite(i, cc)
ans = sum((1 for i in range(26, 26 + n) if uf.nodes[i] < 0))
print(ans)


import sys
input = sys.stdin.readline
n = int(input())
Group = [i for i in range(26)]
Nodes = [1] * 26
USE = [0] * 26

def find(x):
	while Group[x] != x:
		x = Group[x]
	return x

def Union(x, y):
	if find(x) != find(y):
		if Nodes[find(x)] < Nodes[find(y)]:
			Nodes[find(y)] += Nodes[find(x)]
			Nodes[find(x)] = 0
			Group[find(x)] = find(y)
		else:
			Nodes[find(x)] += Nodes[find(y)]
			Nodes[find(y)] = 0
			Group[find(y)] = find(x)
for i in range(n):
	P = input().strip()
	for j in range(1, len(P)):
		Union(ord(P[j - 1]) - 97, ord(P[j]) - 97)
	USE[ord(P[0]) - 97] = 1
SET = set()
for i in range(26):
	if USE[i] == 1:
		SET.add(find(i))
print(len(SET))


n = int(input())
used = []
ekv = []
a = []
for i in range(n):
	s = list(input())
	a.append(s)
a.sort()
j = 1
t = 0
a.append('dsh')
r = 0
for i in range(n - 1):
	r = 0
	for k in range(len(a[i])):
		if a[i][k] in a[i + 1]:
			r = 1
			break
	if r == 0:
		j += 1
print(j)


import sys
lines = sys.stdin.readlines()
n = int(lines[0])
arr = []
for i in range(n):
	cc = lines[i + 1].strip()
	arr.append(''.join(set(cc)))
arr = sorted(arr, key=lambda x: len(x), reverse=True)
cores = []
for cc in arr:
	if len(cores) == 0:
		new_set = set()
		for char in cc:
			new_set.add(char)
		cores.append(new_set)
	else:
		found = False
		for cored in cores:
			for char in cc:
				if char in cored:
					for oneof in cc:
						cored.add(oneof)
					found = True
					break
		if not found:
			new_set = set()
			for char in cc:
				new_set.add(char)
			cores.append(new_set)
for i in range(len(cores)):
	for j in range(i + 1, len(cores)):
		if len(cores[i].intersection(cores[j])) != 0:
			cores[i] = cores[i].union(cores[j])
			cores[j] = set()
cores = list(filter(lambda x: len(x) != 0, cores))
print(len(cores))


import sys
input = lambda : sys.stdin.readline().rstrip('\r\n')

def charToInt(c):
	return ord(c) - ord('a')
n = int(input())
arrs = []
for _ in range(n):
	arrs.append([charToInt(x) for x in input()])
adj = [set() for _ in range(26)]
for string in arrs:
	string2 = set(string)
	for c in string2:
		for d in string2:
			adj[c].add(d)
			adj[d].add(c)
visited = [False for _ in range(26)]
groupCnt = 0
for i in range(26):
	if len(adj[i]) > 0 and visited[i] == False:
		stack = []
		groupCnt += 1
		stack.append(i)
		while stack:
			j = stack.pop()
			visited[j] = True
			for k in adj[j]:
				if visited[k] == False:
					visited[k] = True
					stack.append(k)
print(groupCnt)


def fastio():
	import sys
	from io import StringIO
	from atexit import register
	global input
	sys.stdin = StringIO(sys.stdin.read())
	input = lambda : sys.stdin.readline().rstrip('\r\n')
	sys.stdout = StringIO()
	register(lambda : sys.__stdout__.write(sys.stdout.getvalue()))
fastio()
alpha = list(map(chr, range(97, 123)))
d = {i: 0 for i in alpha}
for z in range(int(input())):
	s = set(input())
	aj = -1
	for i in s:
		if d[i] == z + 1:
			continue
		if d[i] != 0:
			aj = d[i]
		for j in d:
			if d[j] == aj:
				d[j] = z + 1
		d[i] = z + 1
st = set()
for i in d:
	if d[i] != 0:
		st.add(d[i])
print(len(st))


def filtro(aux):
	tamanho = len(aux)
	for i in range(tamanho):
		for j in range(tamanho):
			if i != j:
				for z in aux[i]:
					if z in aux[j]:
						aux[i] += aux[j]
						aux[j] = ''
	return aux
num_de_senhas = int(input())
senhas = [''] * 123
for i in range(num_de_senhas):
	senha = input()
	unicode = ord(senha[0])
	senhas[unicode] += senha
	conjunto = set(senhas[unicode])
	senhas[unicode] = ''.join(conjunto)
senhas_filtradas = filtro(senhas)
min_senhas = 0
for i in senhas_filtradas:
	if i != '':
		min_senhas += 1
print(min_senhas)


def union(x, y):
	xr = find(x)
	yr = find(y)
	root[yr] = xr

def find(x):
	if root[x] != x:
		x = find(root[x])
	return x
import sys
input = sys.stdin.readline
used = set()
root = dict()
for _ in range(int(input())):
	s = input().rstrip()
	orrr = ord(s[0]) - 97
	if orrr not in root:
		root[orrr] = orrr
	for j in range(1, len(s)):
		orr = ord(s[j]) - 97
		if orr not in root:
			root[orr] = orr
		union(orrr, orr)
for i in root:
	used.add(find(root[i]))
print(used.__len__())


import sys
input = sys.stdin.readline
parent = [i for i in range(26)]
presence = [0] * 26
T = int(input())
for n in range(T):
	s = [ord(ch) - 97 for ch in input().strip()]
	p = parent[s[0]]
	for q in s:
		presence[q] = 1
		if parent[q] != p:
			parent[parent[q]] = p
		parent[q] = p
ans = 0
for i in range(26):
	if presence[i] and parent[i] == i:
		ans += 1
print(ans)


import sys
input = sys.stdin.readline
from collections import deque

class Graph(object):

	def __init__(self, n, d):
		self.n = n
		self.graph = [[] for i in range(n)]
		self.parent = [-1 for i in range(n)]
		self.directed = d

	def addEdge(self, x, y):
		self.graph[x].append(y)
		if not self.directed:
			self.graph[y].append(x)

	def bfs(self, root, vis):
		self.parent = [-1 for i in range(self.n)]
		queue = [root]
		queue = deque(queue)
		while len(queue) != 0:
			element = queue.popleft()
			vis[element] = 1
			for i in self.graph[element]:
				if vis[i] == 0:
					queue.append(i)
					self.parent[i] = element

	def dfs(self, root, ans):
		stack = [root]
		vis = [0] * self.n
		stack2 = []
		while len(stack) != 0:
			element = stack.pop()
			if vis[element]:
				continue
			vis[element] = 1
			stack2.append(element)
			for i in self.graph[element]:
				if vis[i] == 0:
					self.parent[i] = element
					stack.append(i)
		while len(stack2) != 0:
			element = stack2.pop()
			m = 0
			for i in self.graph[element]:
				if i != self.parent[element]:
					m += ans[i]
			ans[element] = m
		return ans

	def shortestpath(self, source, dest):
		self.bfs(source)
		path = [dest]
		while self.parent[path[-1]] != -1:
			path.append(parent[path[-1]])
		return path[::-1]

	def ifcycle(self):
		self.bfs(0)
		queue = [0]
		vis = [0] * n
		queue = deque(queue)
		while len(queue) != 0:
			element = queue.popleft()
			vis[element] = 1
			for i in graph[element]:
				if vis[i] == 1 and i != parent[element]:
					return True
				if vis[i] == 0:
					queue.append(i)
					vis[i] = 1
		return False

	def reroot(self, root, ans):
		stack = [root]
		vis = [0] * n
		while len(stack) != 0:
			e = stack[-1]
			if vis[e]:
				stack.pop()
				continue
			vis[e] = 1
			for i in graph[e]:
				if not vis[e]:
					stack.append(i)
			if self.parent[e] == -1:
				continue
n = int(input())
g = Graph(n + 26, False)
a = []
for i in range(n):
	a.append(set(input().strip()))
for i in range(n):
	for j in a[i]:
		g.addEdge(26 + i, ord(j) - 97)
vis = [0] * (n + 26)
ans = 0
for i in range(26, n + 26):
	if vis[i] == 0:
		ans += 1
		g.bfs(i, vis)
print(ans)


n = int(input())
a = [''] * 26
b = [i for i in range(26)]
k = 0
for i in range(n):
	s = input()
	ind = ord(s[0]) - 97
	a[ind] += s
	a[ind] = ''.join(set(a[ind]))
for i in range(len(a)):
	for j in range(len(a)):
		if i != j:
			for ch in a[j]:
				if ch in a[i]:
					a[i] += a[j]
					a[j] = ''
cnt = 0
for i in a:
	if i != '':
		cnt += 1
print(cnt)


import math, sys
from collections import defaultdict, deque
import bisect as bi

def yes():
	print('YES')

def no():
	print('NO')

def I():
	return int(sys.stdin.readline())

def In():
	return map(int, sys.stdin.readline().split())

def Sn():
	return sys.stdin.readline().strip()

def dict(a):
	d = {}
	for x in a:
		if d.get(x, -1) != -1:
			d[x] += 1
		else:
			d[x] = 1
	return d

def find_gt(a, x):
	i = bi.bisect_left(a, x)
	if i != len(a):
		return i
	else:
		return -1

class DSU:

	def __init__(self, n):
		self.parent = [i for i in range(n + 1)]
		self.rank = [0 for i in range(n + 1)]

	def find_set(self, v):
		if v == self.parent[v]:
			return v
		self.parent[v] = self.find_set(self.parent[v])
		return self.parent[v]

	def union(self, a, b):
		a = self.find_set(a)
		b = self.find_set(b)
		if a != b:
			if self.rank[a] < self.rank[b]:
				(a, b) = (b, a)
			self.parent[b] = a
			if self.rank[a] == self.rank[b]:
				self.rank[a] += 1

def main():
	try:
		n = I()
		dsu = DSU(26)
		d = {}
		for i in range(97, 97 + 26):
			d[chr(i)] = 0
		for i in range(n):
			s = Sn()
			d[s[0]] = 1
			for x in range(1, len(s)):
				d[s[x]] = 1
				dsu.union(ord(s[0]) - 96, ord(s[x]) - 96)
		z = {}
		for i in range(97, 97 + 26):
			if d[chr(i)] != 0:
				z1 = dsu.find_set(i - 96)
				if z.get(z1, -1) != -1:
					z[z1] += 1
				else:
					z[z1] = 1
		print(len(z))
	except:
		pass
M = 998244353
P = 1000000007
for _ in range(1):
	main()


import sys

def get(p, x):
	path_x = []
	while x != p[x]:
		path_x.append(x)
		x = p[x]
	return (x, path_x)

def join(p, rank, x, y):
	(x, path_x) = get(p, x)
	(y, path_y) = get(p, y)
	if x == y:
		return False
	else:
		if rank[x] > rank[y]:
			(x, y) = (y, x)
		if rank[x] == rank[y]:
			rank[y] += 1
		p[x] = y
		for el in path_x:
			p[el] = y
		for el in path_y:
			p[el] = y
		return True

def check(p, x, y):
	(x, path_x) = get(p, x)
	(y, path_y) = get(p, y)
	for el in path_x:
		p[el] = x
	for el in path_y:
		p[el] = y
	return x == y

def get_id(c):
	return ord(c) - 97
reader = (s.rstrip() for s in sys.stdin)
n = int(next(reader))
p = list(range(26))
rank = [0] * 26
used = set()
for _ in range(n):
	word = next(reader)
	unq_cs = set(word)
	used.update(unq_cs)
	c1 = unq_cs.pop()
	i1 = get_id(c1)
	while unq_cs:
		c2 = unq_cs.pop()
		i2 = get_id(c2)
		join(p, rank, i1, i2)
unq_gr = set()
for c in used:
	(gr_id, _) = get(p, get_id(c))
	unq_gr.add(gr_id)
print(len(unq_gr))


from sys import stdin, stdout
ufa = []

def ufind(i):
	if ufa[i] != i:
		ufa[i] = ufind(ufa[i])
	return ufa[i]

def union(i, j):
	iu = ufind(i)
	ju = ufind(j)
	ufa[ju] = ufa[iu]
n = int(stdin.readline())
for i in range(26):
	ufa.append(i)
used = [False for i in range(26)]
for i in range(n):
	s = stdin.readline()
	used[ord(s[0]) - 97] = True
	for j in range(1, len(s) - 1):
		if ord(s[0]) - 97 != ord(s[j]) - 97:
			used[ord(s[j]) - 97] = True
			union(ord(s[0]) - 97, ord(s[j]) - 97)
hs = set()
for i in range(26):
	if used[i]:
		hs.add(ufind(ufa[i]))
print(hs.__len__())


d = {}

def f(now):
	if now not in d:
		d[now] = now
		return now
	if d[now] == now:
		return now
	d[now] = f(d[now])
	return d[now]
k = int(input())
F = {frozenset(input()) for _ in range(k)}
for a in F:
	a = list(a)
	top = a[0]
	f(top)
	for x in a[1:]:
		d[f(x)] = f(top)
s = set()
for x in d:
	s.add(f(x))
print(len(s))


import os
import sys
from io import BytesIO, IOBase
import heapq as h
from types import GeneratorType
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		import os
		self.os = os
		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 = self.os.read(self._fd, max(self.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 = self.os.read(self._fd, max(self.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:
			self.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')
import time
start_time = time.time()
import collections as col
import math, string
from functools import reduce

def getInts():
	return [int(s) for s in input().split()]

def getInt():
	return int(input())

def getStrs():
	return [s for s in input().split()]

def getStr():
	return input()

def listStr():
	return list(input())

def solve():
	N = getInt()
	nodes = set()
	graph = col.defaultdict(set)
	for n in range(N):
		wd = getStr()
		if len(wd) == 1:
			nodes.add(wd[0])
		for i in range(len(wd) - 1):
			(u, v) = (wd[i], wd[i + 1])
			nodes.add(u)
			nodes.add(v)
			graph[u].add(v)
			graph[v].add(u)
	ans = 0

	def dfs(node):
		if node in nodes:
			nodes.remove(node)
		for neighbour in graph[node]:
			if neighbour in nodes:
				dfs(neighbour)
		return
	while nodes:
		node = next(iter(nodes))
		dfs(node)
		ans += 1
	return ans
print(solve())


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

class UnionFind:

	def __init__(self, N):
		self.parent = [i for i in range(N)]
		self.size = [1 for _ in range(N)]

	def find(self, x):
		if self.parent[x] == x:
			return x
		else:
			return self.find(self.parent[x])

	def union(self, x, y):
		px = self.find(x)
		py = self.find(y)
		if px == py:
			return
		if self.size[px] < self.size[py]:
			self.parent[px] = py
			self.size[py] += self.size[px]
		else:
			self.parent[py] = px
			self.size[px] += self.size[py]

	def same(self, x, y):
		return self.find(x) == self.find(y)

	def connectedNum(self, x):
		return self.size[self.find(x)]

	def is_connected(self):
		return self.connectedNum(0) == len(self.parent)

	def num_comp(self):
		ret = set()
		for i in range(len(self.size)):
			ret.add(self.find(i))
		return len(ret)
N = int(input())
passwords = [set(input()[:-1]) for _ in range(N)]
UN = UnionFind(26)
emerge = set()
for (i, p) in enumerate(passwords):
	f = list(p)[0]
	for c in p:
		emerge.add(c)
		UN.union(ord(f) - ord('a'), ord(c) - ord('a'))
print(len(emerge) - (26 - UN.num_comp()))


import sys
input = sys.stdin.readline
n = int(input())
parent = list(range(n + 26))

def findAncestor(a):
	if parent[a] != a:
		parent[a] = findAncestor(parent[a])
	return parent[a]
for i in range(n):
	s = [ord(ch) - 97 for ch in set(input().strip())]
	for q in s:
		parent[findAncestor(q)] = i + 26
ans = 0
for i in range(26, n + 26):
	if parent[i] == i:
		ans += 1
print(ans)


from sys import stdin
inp = lambda : stdin.readline().strip()
n = int(inp())

def dfs(visited, graph, node):
	if not visited[node]:
		visited[node] = True
		for neighbour in graph[node]:
			dfs(visited, graph, neighbour)
adj = [set() for x in range(26)]
visited = [True] * 26
for i in range(n):
	s = list(set(inp()))
	for j in range(len(s)):
		visited[ord(s[j]) - 97] = False
		for k in range(len(s)):
			if s[k] != s[j]:
				adj[ord(s[j]) - 97].add(ord(s[k]) - 97)
				adj[ord(s[k]) - 97].add(ord(s[j]) - 97)
for i in range(26):
	adj[i] = list(adj[i])
counter = 0
for i in range(26):
	if not visited[i]:
		dfs(visited, adj, i)
		counter += 1
print(counter)


n = int(input())
arr = set()
for _ in range(n):
	arr.add(''.join(set(input())))
arr = list(arr)
cnt = 0
for i in range(len(arr)):
	for j in range(len(arr)):
		if i != j:
			if len(set(arr[i]).intersection(set(arr[j]))) > 0:
				cnt += 1
				break
if cnt == 0:
	print(len(arr) - cnt)
else:
	print(len(arr) - cnt + 1)


from sys import stdin, exit
from typing import List, Tuple

class Password:
	string: str
	handle: int

	def __init__(self, p: str, h: int):
		self.string = p
		self.handle = h
pass_list_len = int(stdin.readline().rstrip())
pass_list = [None] * pass_list_len
dsu_parent = [-1] * pass_list_len
dus_size = [0] * pass_list_len

def make_set(v: int):
	dsu_parent[v] = v
	dus_size[v] = 1

def find_set(v: int):
	if dsu_parent[v] == v:
		return v
	dsu_parent[v] = find_set(dsu_parent[v])
	return dsu_parent[v]

def union_sets(v1: int, v2: int):
	v1 = find_set(v1)
	v2 = find_set(v2)
	if v1 != v2:
		if dus_size[v2] < dus_size[v1]:
			(v1, v2) = (v2, v1)
		dsu_parent[v1] = v2
		dus_size[v2] += dus_size[v1]
includes_char = dict()
for i in range(pass_list_len):
	p = Password(stdin.readline().rstrip(), i)
	pass_list[i] = p
	make_set(p.handle)
	for c in p.string:
		if c not in includes_char:
			includes_char[c] = {p.handle}
		else:
			includes_char[c].add(p.handle)
for pass_set in includes_char.values():
	last_h = None
	for p in pass_set:
		if last_h is not None:
			union_sets(last_h, p)
		last_h = p
for passs in pass_list:
	for c in passs.string:
		union_sets(passs.handle, next(iter(includes_char[c])))
print(len({find_set(p.handle) for p in pass_list}))


class UnionFind:

	def __init__(self, n):
		self.n = n
		self.root = [-1] * (n + 1)
		self.rnk = [0] * (n + 1)

	def Find_Root(self, x):
		if self.root[x] < 0:
			return x
		else:
			self.root[x] = self.Find_Root(self.root[x])
			return self.root[x]

	def Unite(self, x, y):
		x = self.Find_Root(x)
		y = self.Find_Root(y)
		if x == y:
			return
		elif self.rnk[x] > self.rnk[y]:
			self.root[x] += self.root[y]
			self.root[y] = x
		else:
			self.root[y] += self.root[x]
			self.root[x] = y
			if self.rnk[x] == self.rnk[y]:
				self.rnk[y] += 1

	def isSameGroup(self, x, y):
		return self.Find_Root(x) == self.Find_Root(y)

	def Count(self, x):
		return -self.root[self.Find_Root(x)]
import sys
input = sys.stdin.readline
N = int(input())
uni = UnionFind(N)
dic = {}
for i in range(N):
	S = input().rstrip()
	for s in S:
		if not s in dic:
			dic[s] = [i]
		else:
			dic[s].append(i)
for v in dic.values():
	for k in v:
		uni.Unite(k, v[0])
root = set()
for n in range(N):
	root.add(uni.Find_Root(n))
print(len(root))


import sys
from collections import deque

def equivalent(password, char):
	for c in password:
		if c == char:
			return True
	return False

def solve():
	input = sys.stdin.readline
	n = int(input())
	alphabet = [chr(_) for _ in range(97, 123)]
	graph = {i: list() for i in range(n)}
	checked = {i: False for i in range(n)}
	for char in alphabet:
		graph[char] = list()
		checked[char] = False
	passwords = [input().strip() for _ in range(n)]
	for char in alphabet:
		for i in range(n):
			if equivalent(passwords[i], char):
				graph[char].append(i)
				graph[i].append(char)
	cnt = 0
	for i in range(n):
		if not checked[i]:
			cnt += 1
			checked[i] = True
			q = deque()
			q.append(i)
			while q:
				cur = q.popleft()
				checked[cur] = True
				for neighbor in graph[cur]:
					if not checked[neighbor]:
						checked[neighbor] = True
						q.append(neighbor)
	print(cnt)
solve()


from sys import stdin
mx = 200005
par = [None] * mx

def build(n):
	for i in range(n + 1):
		par[i] = i

def find(n):
	if par[n] == n:
		return n
	else:
		return find(par[n])

def union(a, b):
	par[find(b)] = find(a)
n = int(stdin.readline())
build(300)
mp = [False] * 300
for x in range(n):
	s = stdin.readline()
	mp[ord(s[0])] = True
	for i in range(1, len(s) - 1):
		union(ord(s[0]), ord(s[i]))
		mp[ord(s[i])] = True
l = []
for i in range(300):
	if mp[i] == True:
		l.append(find(i))
s = set(l)
print(len(s))


import sys
input = sys.stdin.readline
n = int(input())
f = []
d = []
for i in range(0, 26):
	d.append([0] * 26)
use = [0] * 26
for i in range(0, 26):
	f.append(i)

def find(x):
	if x != f[x]:
		f[x] = find(f[x])
	return f[x]
for i in range(0, n):
	l = []
	for j in set(input().rstrip()):
		use[ord(j) - 97] = 1
		l.append(ord(j) - 97)
	ll = len(l)
	for k in range(0, ll):
		for j in range(k + 1, ll):
			d[l[k]][l[j]] = 1
for i in range(0, 26):
	for j in range(0, 26):
		if d[i][j]:
			if find(i) != find(j):
				f[find(i)] = find(j)
ans = []
a = 0
for i in range(0, 26):
	if use[i] == 1 and find(i) not in ans:
		ans.append(find(i))
		a += 1
print(a)


from collections import defaultdict, deque as dq
from sys import stdin
input = stdin.readline

def f(q):
	A = ord('a')
	g = defaultdict(set)
	vis = [1] * 26
	for _ in range(q):
		word = input().strip()
		word = list(set(word))
		for i in range(len(word)):
			vis[ord(word[i]) - A] = 0
			for j in range(i):
				g[ord(word[i]) - A].add(ord(word[j]) - A)
				g[ord(word[j]) - A].add(ord(word[i]) - A)
				vis[ord(word[j]) - A] = 0
	cnt = 0
	for i in range(26):
		if vis[i] == 0:
			cnt += 1
			q = dq([i])
			while q:
				t = q.popleft()
				for i in g[t]:
					if vis[i] == 0:
						vis[i] = 1
						q.append(i)
	return cnt
print(f(int(input())))


import time
import collections
import string
used = set()
g = collections.defaultdict(lambda : set())
LOCAL = False
start_time = None

def dfs(v):
	used.add(v)
	for w in g[v]:
		if w not in used:
			dfs(w)
if LOCAL:
	start_time = time.time()
n = int(input())
used_letters = set()
for _ in range(n):
	pwd = input()
	for i in pwd:
		g[i].add(pwd)
	a = set(pwd)
	g[pwd] = a
cnt = 0
for ch in string.ascii_lowercase:
	if ch in g and ch not in used:
		dfs(ch)
		cnt += 1
print(cnt)
if LOCAL:
	print('--- %s seconds ---' % (time.time() - start_time))


import sys
input = sys.stdin.readline
n = int(input())
par = [i for i in range(n + 26)]

def find(x):
	if par[x] != x:
		par[x] = find(par[x])
	return par[x]
for i in range(n):
	for j in set(input().rstrip()):
		par[find(ord(j) - 97)] = find(i + 26)
ans = 0
for i in range(26, n + 26):
	if par[i] == i:
		ans += 1
print(ans)


import math
import time
from collections import defaultdict, deque
from sys import stdin, stdout
from bisect import bisect_left, bisect_right
from queue import PriorityQueue
import sys

class Graph:

	def __init__(self, n):
		self.graph = defaultdict(lambda : [])
		self.vertices = n
		self.edges = 0
		self.toposorted = []

	def addEdge(self, a, b):
		self.graph[a].append(b)
		self.edges += 1

	def cycleUntill(self, visited, curr, parent):
		visited[curr] = True
		for i in self.graph[curr]:
			if not visited[i]:
				if self.cycleUntill(visited, i, curr):
					return True
			elif i != parent:
				return True
		return False

	def cycle(self):
		n = self.vertices
		visited = [False] * (n + 2)
		for i in range(1, n + 1):
			if not visited[i]:
				if self.cycleUntill(visited, i, -1):
					return True
		return False

	def topologicalSort(self):
		in_degree = [0] * (self.vertices + 10)
		for i in self.graph:
			for j in self.graph[i]:
				in_degree[j] += 1
		queue = deque()
		for i in range(1, self.vertices + 1):
			if in_degree[i] == 0:
				queue.append(i)
		cnt = 0
		while queue:
			u = queue.popleft()
			self.toposorted.append(u)
			for i in self.graph[u]:
				in_degree[i] -= 1
				if in_degree[i] == 0:
					queue.append(i)
			cnt += 1
		if cnt != self.vertices:
			return False
		else:
			return True

	def connected(self):
		visited = [False] * (self.vertices + 2)
		ans = []
		for i in range(1, self.vertices + 1):
			if not visited[i]:
				comp = []
				q = deque()
				visited[i] = True
				q.append(i)
				while len(q) > 0:
					temp = q.popleft()
					comp.append(temp)
					for j in self.graph[temp]:
						if not visited[j]:
							visited[j] = True
							q.append(j)
				ans.append(comp)
		return ans
n = int(stdin.readline())
g = Graph(26)
exist = [0] * 26
for _ in range(n):
	s = stdin.readline().strip('\n')
	exist[ord(s[0]) - 97] += 1
	for i in range(1, len(s)):
		exist[ord(s[i]) - 97] += 1
		if s[i] != s[i - 1]:
			g.addEdge(ord(s[i]) - 96, ord(s[i - 1]) - 96)
			g.addEdge(ord(s[i - 1]) - 96, ord(s[i]) - 96)
count = 0
for i in exist:
	if i == 0:
		count += 1
ans = len(g.connected()) - count
print(ans)


n = int(input())
visiteds = {}
aux = {}
count = 0

def dfs(value):
	if not value in visiteds:
		visiteds[value] = True
		for key in aux:
			if value in aux[key]:
				dfs(key)

def add_dict(array, dictionary):
	for element in array:
		dictionary[element] = True
	return dictionary
for i in range(n):
	row = list(frozenset(list(input())))
	if row[0] in aux:
		add_dict(row, aux[row[0]])
	else:
		aux[row[0]] = add_dict(row, {})
for letter in aux:
	if not letter in visiteds:
		count += 1
		for char in aux:
			if letter in aux[char]:
				for element in aux[char]:
					dfs(element)
print(count)


from sys import stdin, stdout

def dfs(s):
	visited[s] = 1
	for i in range(len(adj[s])):
		if visited[adj[s][i]] == 0:
			dfs(adj[s][i])
adj = [[] for i in range(26)]
globalPresent = [0] * 26
for t in range(int(input())):
	s = stdin.readline().strip()
	present = [0] * 26
	a = ord(s[0]) - ord('a')
	present[a] = 1
	globalPresent[a] = 1
	for i in range(1, len(s)):
		b = ord(s[i]) - ord('a')
		if present[b] == 0:
			present[b] = 1
			globalPresent[b] = 1
			adj[a].append(b)
			adj[b].append(a)
visited = [0] * 26
ct = 0
for i in range(26):
	if visited[i] == 0 and globalPresent[i]:
		ct += 1
		dfs(i)
print(ct)


from bisect import bisect_right
from bisect import bisect_left
from collections import defaultdict
from math import sqrt, factorial, gcd, log2, inf, ceil
import sys
input = sys.stdin.readline

def find_parent(u):
	if par[u] != u:
		par[u] = find_parent(par[u])
	return par[u]
n = int(input())
la = []
hash = defaultdict(list)
par = [0] + [i + 1 for i in range(26)]
rank = [1] * (26 + 1)
seti = set()
booli = [False] * 27
for i in range(n):
	z = input()
	z = z[:len(z) - 1]
	k = min(z)
	set1 = set(min(k))
	booli[ord(k) - 97 + 1] = True
	a = ord(k) - 97 + 1
	z2 = find_parent(a)
	for i in range(len(z)):
		booli[ord(z[i]) - 97 + 1] = True
		if z[i] not in set1:
			b = ord(z[i]) - 97 + 1
			z1 = find_parent(b)
			if z1 != z2:
				par[z1] = z2
			set1.add(i)
ans = set()
for i in range(26):
	if booli[i + 1] == True:
		z = find_parent(i + 1)
		ans.add(z)
print(len(ans))


from sys import stdin
input = stdin.readline
N = int(200000.0 + 3)
n = int(input())
arr = list((input() for _ in range(n)))
rnk = [1 for i in range(N + 33)]
prt = [i for i in range(N + 33)]

def find_node(i):
	if i == prt[i]:
		return i
	return find_node(prt[i])

def union_nodes(i, j):
	pi = find_node(i)
	pj = find_node(j)
	if pi == pj:
		return
	if rnk[pi] < rnk[pj]:
		prt[pi] = pj
		rnk[pj] += 1
	else:
		prt[pj] = pi
		rnk[pi] += 1
for i in range(26):
	for j in range(n):
		if chr(ord('a') + i) in arr[j]:
			union_nodes(j, N + i)
res = sum([int(i == prt[i]) for i in range(n)])
print(res)


import sys
input = sys.stdin.readline
m = 26
uf = [-1] * m

def root(x):
	if uf[x] < 0:
		return x
	uf[x] = root(uf[x])
	return uf[x]

def unite(x, y):
	(rx, ry) = (root(x), root(y))
	if rx == ry:
		return False
	if uf[rx] > uf[ry]:
		(rx, ry) = (ry, rx)
	uf[rx] += uf[ry]
	uf[ry] = rx
	return True
n = int(input())
used = [0] * m
for i in range(n):
	s = input()[:-1]
	s = list(set((ord(c) - ord('a') for c in s)))
	l = len(s)
	for j in range(l):
		used[s[j]] = 1
		for k in range(j + 1, l):
			unite(s[j], s[k])
ans = sum((uf[i] < 0 and used[i] for i in range(m)))
print(ans)


import time
JA = time.time()
n = int(input())

def get_input():
	return list(set([frozenset(input()) for _ in range(n)]))
passwords = get_input()
graph = [[] for _ in range(26 + len(passwords))]
for i in range(0, 26):
	for j in range(26, 26 + len(passwords)):
		if chr(i + 97) in passwords[j - 26]:
			graph[i].append(j)
			graph[j].append(i)

def dfs_helper(i, visited):
	visited[i] = True
	for neighbour in graph[i]:
		if neighbour not in visited:
			dfs_helper(neighbour, visited)

def dfs():
	visited = {}
	ccp = 0
	for i in range(26, len(graph)):
		if i not in visited:
			dfs_helper(i, visited)
			ccp += 1
	print(ccp)
dfs()


import sys
input = sys.stdin.readline

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

def find(parentGroup, index):
	if parentGroup[index] == 'N':
		return 'N'
	elif ord('a') + index == ord(parentGroup[index]):
		return parentGroup[index]
	else:
		parentGroup[index] = find(parentGroup, mapCharToIndex(parentGroup[index]))
		return parentGroup[index]
n = int(input())
parentGroup = ['N'] * 26
for i in range(n):
	value = input().rstrip()
	groups = []
	chars = []
	for c in set(value):
		grp = find(parentGroup, mapCharToIndex(c))
		if grp == 'N':
			chars.append(c)
		else:
			groups.append(grp)
	if len(groups) == 0:
		newGrp = chars[0]
	else:
		newGrp = groups[0]
	for c in chars:
		parentGroup[mapCharToIndex(c)] = newGrp
	if len(groups) > 1:
		for gr in groups[1:]:
			parentGroup[mapCharToIndex(gr)] = newGrp
ans = 0
for index in range(0, 26):
	if parentGroup[index] != 'N' and ord('a') + index == ord(parentGroup[index]):
		ans += 1
print(ans)


import sys
import math
import bisect
from sys import stdin, stdout
from math import gcd, floor, sqrt, log2, ceil
from collections import defaultdict as dd
from bisect import bisect_left as bl, bisect_right as br
from bisect import insort
from collections import Counter
from collections import deque
from heapq import heappush, heappop, heapify
from itertools import permutations, combinations
mod = int(1000000000.0) + 7
ip = lambda : int(stdin.readline())
inp = lambda : map(int, stdin.readline().split())
ips = lambda : stdin.readline().rstrip()
out = lambda x: stdout.write(str(x) + '\n')

def addEdge(a, b):
	graph[a].append(b)
	graph[b].append(a)

def Bfs(src):
	q = deque([])
	q.append(src)
	visited[src] = True
	while q:
		node = q.popleft()
		for neighbour in graph[node]:
			if not visited[neighbour]:
				q.append(neighbour)
				visited[neighbour] = True
t = 1
for _ in range(t):
	n = ip()
	arr = []
	for i in range(n):
		s = ips()
		s = ''.join(list(set(s)))
		arr.append(s)
	mark = dd(bool)
	graph = [[] for i in range(n)]
	ch = 'abcdefghijklmnopqrstuvwxyz'
	for i in ch:
		mark = []
		for j in range(n):
			if i in arr[j]:
				mark.append(j)
		for i in range(1, len(mark)):
			addEdge(mark[0], mark[i])
	visited = [False] * n
	ans = 0
	for i in range(n):
		if not visited[i]:
			ans += 1
			Bfs(i)
	out(ans)


def dfs(n):
	global visited
	global chars
	visited[n] = True
	adj = list(set(chars.get(n, [])))
	for node in adj:
		if not visited[node]:
			dfs(node)
import sys
inp = sys.stdin.buffer.read().split()
ii = 0
n = int(inp[ii])
ii += 1
chars = {}
visited = {}
while n:
	n -= 1
	s = list(set(inp[ii]))
	ii += 1
	flag = 0
	if not s[0] in chars:
		chars[s[0]] = []
		visited[s[0]] = False
	for i in range(1, len(s)):
		chars[s[0]].append(s[i])
		if not s[i] in chars:
			chars[s[i]] = [s[0]]
		else:
			chars[s[i]].append(s[0])
		visited[s[i]] = False
ans = 0
for (k, v) in chars.items():
	if not visited[k]:
		dfs(k)
		ans += 1
print(ans)


from sys import stdin

class DisjointUnionSet:

	def __init__(self, size):
		self.parent = list(range(size))
		self.rank = [0] * size

	def find_set(self, v):
		if v == self.parent[v]:
			return v
		self.parent[v] = self.find_set(self.parent[v])
		return self.parent[v]

	def union_sets(self, a, b):
		a = self.find_set(a)
		b = self.find_set(b)
		if a != b:
			if self.rank[a] < self.rank[b]:
				(a, b) = (b, a)
			self.parent[b] = a
			if self.rank[a] == self.rank[b]:
				self.rank[a] += 1

	def count_sets(self):
		res = 0
		for i in range(len(self.parent)):
			if self.parent[i] == i:
				res += 1
		return res

def input():
	return next(stdin)

def main():
	n = int(input())
	du = DisjointUnionSet(n)
	chars = [-1] * 26
	for i in range(n):
		ss = list(input())[:-1]
		for (j, c) in enumerate(ss):
			a = ord(c) - ord('a')
			if chars[a] == -1:
				chars[a] = i
			else:
				du.union_sets(chars[a], i)
	print(du.count_sets())
main()


def union(a, b):
	a = find(a)
	b = find(b)
	lt[b] = a

def find(x):
	if lt[x] != x:
		x = find(lt[x])
	return x
import sys
input = sys.stdin.readline
n = int(input())
lt = dict()
for i in range(n):
	s = input().rstrip()
	for j in s:
		if j not in lt:
			lt[j] = j
		union(s[0], j)
ans = set()
for i in lt:
	ans.add(find(i))
print(len(ans))


arr = [-1 for i in range(26)]

def union(a, b):
	pa = get(a)
	pb = get(b)
	arr[pa] = pb

def get(a):
	x = a if arr[a] == a else get(arr[a])
	arr[a] = x
	return x

def ask(x):
	return ord(x) - ord('a')
n = int(input())
v = set()
for _ in range(n):
	s = ''.join(set(input()))
	if s in v:
		continue
	v.add(s)
	if len(s) == 1:
		if arr[ask(s[0])] == -1:
			arr[ask(s[0])] = ask(s[0])
	for i in range(len(s) - 1):
		if arr[ask(s[i])] == -1:
			arr[ask(s[i])] = ask(s[i])
		if arr[ask(s[i + 1])] == -1:
			arr[ask(s[i + 1])] = ask(s[i + 1])
		union(ask(s[i]), ask(s[i + 1]))
cnt = 0
for i in range(26):
	if arr[i] == i:
		cnt += 1
print(cnt)


letterDict = {}
for _ in [0] * int(input()):
	tmp = input()
	for c in tmp:
		if c not in letterDict.keys():
			letterDict[c] = {c}
	for (a, b) in zip(tmp, tmp[1:]):
		if b not in letterDict[a]:
			d = letterDict[b]
			letterDict[a].update(d)
			for c in d:
				letterDict[c] = letterDict[a]
x = set()
ans = 0
for e in letterDict.keys():
	if e not in x:
		ans += 1
		x.update(letterDict[e])
print(ans)


import sys
input = sys.stdin.readline
n = int(input())

def MakeSet(x):
	x.parent = x
	x.rank = 0

def Union(x, y):
	xRoot = Find(x)
	yRoot = Find(y)
	if xRoot.rank > yRoot.rank:
		yRoot.parent = xRoot
	elif xRoot.rank < yRoot.rank:
		xRoot.parent = yRoot
	elif xRoot != yRoot:
		yRoot.parent = xRoot
		xRoot.rank = xRoot.rank + 1

def Find(x):
	if x.parent == x:
		return x
	else:
		x.parent = Find(x.parent)
		return x.parent

class Node:

	def __init__(self, label):
		self.label = label

	def __str__(self):
		return self.label
used = [0] * 26
nodes = [Node(ch) for ch in range(26)]
[MakeSet(node) for node in nodes]
for _ in range(n):
	s = input().strip()
	prev = -1
	for c in s:
		val = ord(c) - 97
		used[val] = 1
		if prev != -1:
			Union(nodes[prev], nodes[val])
		prev = val
outs = [0] * 26
for node in nodes:
	outs[Find(node).label] += 1
count = 0
for val in outs:
	if val != 0:
		count += 1
print(count + sum(used) - 26)


import sys

def rint():
	return map(int, sys.stdin.readline().split())

def input():
	return sys.stdin.readline().rstrip('\n')

def oint():
	return int(input())
n = oint()
node = dict()
for _ in range(n):
	s = set(input())
	for c1 in s:
		if not c1 in node:
			node[c1] = s
		else:
			node[c1] = node[c1].union(s)
v = set()

def dfs(k):
	if k in v:
		return
	v.add(k)
	for kk in node[k]:
		if not kk in v:
			dfs(kk)
	return
ans = 0
for k in node:
	if k in v:
		continue
	ans += 1
	dfs(k)
print(ans)


import sys, os, io

def rs():
	return sys.stdin.readline().rstrip()

def ri():
	return int(sys.stdin.readline())

def ria():
	return list(map(int, sys.stdin.readline().split()))

def ws(s):
	sys.stdout.write(s + '\n')

def wi(n):
	sys.stdout.write(str(n) + '\n')

def wia(a):
	sys.stdout.write(' '.join([str(x) for x in a]) + '\n')
import math, datetime, functools, itertools, operator, bisect, fractions, statistics
from collections import deque, defaultdict, OrderedDict, Counter
from fractions import Fraction
from decimal import Decimal
from sys import stdout
from heapq import heappush, heappop, heapify, _heapify_max, _heappop_max, nsmallest, nlargest
sys.setrecursionlimit(111111)
INF = 99999999999999999999999999999999

def main():
	mod = 1000000007
	starttime = datetime.datetime.now()
	if os.path.exists('input.txt'):
		sys.stdin = open('input.txt', 'r')
		sys.stdout = open('output.txt', 'w')
	tc = 1
	for _ in range(tc):
		n = ri()
		z = []
		visited = [False] * 26
		graph = [[] for i in range(26)]
		d = {}
		for i in range(n):
			z.append(list(set(list(rs()))))
		for i in range(n):
			if len(z[i]) == 1:
				d[ord(z[i][0]) - 97] = 1
			for j in range(len(z[i]) - 1):
				(a, b) = (ord(z[i][j]) - 97, ord(z[i][j + 1]) - 97)
				d[a] = 1
				d[b] = 1
				graph[a].append(b)
				graph[b].append(a)

		def dfs(start):
			stack = [start]
			while stack:
				start = stack[-1]
				if not visited[start]:
					visited[start] = True
					for child in graph[start]:
						if not visited[child]:
							stack.append(child)
				else:
					stack.pop()
		cc = 0
		for i in range(26):
			if i in d and (not visited[i]):
				cc += 1
				dfs(i)
		wi(cc)
	endtime = datetime.datetime.now()
	time = (endtime - starttime).total_seconds() * 1000
	if os.path.exists('input.txt'):
		print('Time:', time, 'ms')

class FastReader(io.IOBase):
	newlines = 0

	def __init__(self, fd, chunk_size=1024 * 8):
		self._fd = fd
		self._chunk_size = chunk_size
		self.buffer = io.BytesIO()

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size))
			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, size=-1):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size if size == -1 else size))
			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()

class FastWriter(io.IOBase):

	def __init__(self, fd):
		self._fd = fd
		self.buffer = io.BytesIO()
		self.write = self.buffer.write

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

class FastStdin(io.IOBase):

	def __init__(self, fd=0):
		self.buffer = FastReader(fd)
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')

class FastStdout(io.IOBase):

	def __init__(self, fd=1):
		self.buffer = FastWriter(fd)
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.flush = self.buffer.flush
sys.stdin = FastStdin()
sys.stdout = FastStdout()
main()


import os, sys
from io import BytesIO, IOBase

def find(dsu, x):
	if x == dsu[x]:
		return x
	dsu[x] = find(dsu, dsu[x])
	return dsu[x]

def union_by_rank(dsu, a, b, rank):
	(a, b) = (find(dsu, a), find(dsu, b))
	if rank[a] > rank[b]:
		dsu[b] = a
	elif rank[b] > rank[a]:
		dsu[a] = b
	else:
		dsu[a] = b
		rank[a] += 1

def main():
	n = int(input())
	dsu = [i for i in range(26)]
	rank = [1] * 26
	arr = [0] * 26
	for _ in range(n):
		x = list(set(input().strip()))
		for j in range(len(x)):
			arr[ord(x[j]) - 97] = 1
			for k in range(j + 1, len(x)):
				union_by_rank(dsu, ord(x[j]) - 97, ord(x[k]) - 97, rank)
	print(len(set([find(dsu, x) for x in range(26)])) - 26 + arr.count(1))
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')
main()


from sys import stdin, stdout
from collections import defaultdict, deque

def get_map(password):
	mymap = defaultdict(list)
	for i in range(26):
		for j in range(len(password)):
			if chr(ord('a') + i) in password[j]:
				mymap[chr(ord('a') + i)].append(j)
	return mymap

def find_parent(node):
	t = node
	while parent[node] != node:
		node = parent[node]
	parent[t] = node
	return node
n = int(stdin.readline().strip())
password = []
parent = dict()
for _ in range(n):
	password.append(stdin.readline().strip())
	parent[_] = _
mymap = get_map(password)
for i in mymap:
	for j in range(1, len(mymap[i])):
		p1 = find_parent(mymap[i][j - 1])
		p2 = find_parent(mymap[i][j])
		parent[p2] = p1
c = 0
for i in parent:
	if parent[i] == i:
		c += 1
print(c)


import sys
input = sys.stdin.readline
from collections import defaultdict

class Unionfind:

	def __init__(self, n):
		self.par = [-1] * n
		self.rank = [1] * n

	def root(self, x):
		p = x
		while not self.par[p] < 0:
			p = self.par[p]
		while x != p:
			tmp = x
			x = self.par[x]
			self.par[tmp] = p
		return p

	def unite(self, x, y):
		(rx, ry) = (self.root(x), self.root(y))
		if rx == ry:
			return False
		if self.rank[rx] < self.rank[ry]:
			(rx, ry) = (ry, rx)
		self.par[rx] += self.par[ry]
		self.par[ry] = rx
		if self.rank[rx] == self.rank[ry]:
			self.rank[rx] += 1

	def is_same(self, x, y):
		return self.root(x) == self.root(y)

	def count(self, x):
		return -self.par[self.root(x)]
n = int(input())
uf = Unionfind(n + 26)
d = defaultdict(int)
c = 0
for alpha in 'abcdefghijklmnopqrstuvwxyz':
	d[alpha] = c
	c += 1
for i in range(n):
	s = input()[:-1]
	for si in s:
		j = d[si]
		uf.unite(i + 26, j)
rs = set((uf.root(i) for i in range(26, n + 26)))
print(len(rs))


import sys
input = sys.stdin.readline
n = int(input())
p = [i for i in range(n + 26)]

def find(x):
	if p[x] != x:
		p[x] = find(p[x])
	return p[x]
for i in range(n):
	for c in input().rstrip():
		p[find(ord(c) - 97)] = find(26 + i)
ans = 0
for i in range(26, n + 26):
	if p[i] == i:
		ans += 1
print(ans)


from collections import deque
n = int(input())
dic = [[] for x in range(n + 1000)]
visited = [False] * (n + 1000)
for i in range(n):
	s = list(set(list(input())))
	for e in s:
		aux = n + ord(e)
		dic[i].append(aux)
		dic[aux].append(i)

def dfs(v):
	visited[v] = True
	for e in dic[v]:
		if not visited[e]:
			dfs(e)
resp = 0
for i in range(n):
	if not visited[i]:
		resp += 1
		dfs(i)
print(resp)


from sys import stdin, stdout
from math import floor, gcd, fabs, factorial, fmod, sqrt, inf, log
from collections import defaultdict as dd, deque
from heapq import merge, heapify, heappop, heappush, nsmallest
from bisect import bisect_left as bl, bisect_right as br, bisect
mod = pow(10, 9) + 7
mod2 = 998244353

def inp():
	return stdin.readline().strip()

def iinp():
	return int(inp())

def out(var, end='\n'):
	stdout.write(str(var) + '\n')

def outa(*var, end='\n'):
	stdout.write(' '.join(map(str, var)) + end)

def lmp():
	return list(mp())

def mp():
	return map(int, inp().split())

def smp():
	return map(str, inp().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(m, val) for j in range(n)]

def remadd(x, y):
	return 1 if x % y else 0

def ceil(a, b):
	return (a + b - 1) // b

def isprime(x):
	if x <= 1:
		return False
	if x in (2, 3):
		return True
	if x % 2 == 0:
		return False
	for i in range(3, int(sqrt(x)) + 1, 2):
		if x % i == 0:
			return False
	return True

def dfs(i):
	global used, md
	for j in md[i]:
		if not used[j]:
			used[j] = True
			dfs(j)
n = iinp()
ml = []
md = l2d(n + 26, 0)
for i in range(n):
	s = inp()
	for c in s:
		j = n + ord(c) - 97
		ml.append((i, j))
		md[i].append(j)
		md[j].append(i)
c = 0
used = l1d(n + 26, False)
for i in range(n + 26):
	if not used[i] and md[i]:
		used[i] = True
		dfs(i)
		c += 1
print(c)


from sys import stdin

def main():
	(_, s) = (input(), stdin.read())
	d = {c: c for c in set(s) - {'\n'}}
	f = d.get
	for w in s.splitlines():
		a = min(map(f, w))
		for ch in w:
			while True:
				b = d[ch]
				if a == b:
					break
				d[ch] = a
				ch = b
	for a in sorted(d.keys()):
		d[a] = d[d[a]]
	print(len(set(d.values())))
main()


n = int(input())
s = set()
for i in range(n):
	s.add(''.join(map(str, set(input()))))
l = list(s)
N = len(l)
ans = 0
for i in range(N):
	for j in range(N):
		if i != j:
			if len(set(l[i]).intersection(set(l[j]))) > 0:
				ans += 1
				break
if ans == 0:
	if N > 26:
		print(26)
	else:
		print(N)
elif N - ans + 1 > 26:
	print(26)
else:
	print(N - ans + 1)


import string
n = int(input())
F = {frozenset(input()) for _ in range(n)}
D = {letter: set() for letter in string.ascii_lowercase}
for f in F:
	for letter in f:
		D[letter].update(f)
changed = True
while changed:
	changed = False
	for s in D.values():
		update_keys = set()
		for letter in s:
			if D[letter] is not s:
				update_keys.add(letter)
		if update_keys:
			ss = s
			changed = True
			break
	if changed:
		for letter in update_keys:
			ss.update(D[letter])
			D[letter] = ss
print(len({id(s) for s in D.values() if s}))


def val(x):
	return ord(x) - ord('a') + 1
G = {}
visited = [False for i in range(27)]
count = [False for i in range(27)]
password = {}
for step in range(int(input())):
	inp = ''.join(sorted(list(input())))
	if inp not in password:
		password[inp] = 1
password = list(password.keys())
for value in range(len(password)):
	x = password[value]
	x = set(x)
	v = val(x.pop())
	if v not in G:
		G[v] = []
		count[v] = True
	for j in x:
		if val(j) not in G:
			G[val(j)] = []
			count[val(j)] = True
		if val(j) not in G[v]:
			G[v].append(val(j))
		if v not in G[val(j)]:
			G[val(j)].append(v)
i = m = 1
stack = [i]
while i < 27:
	while len(stack) > 1:
		v = stack.pop()
		visited[v] = True
		for e in G[v]:
			if not visited[e]:
				stack.append(e)
				visited[e] = True
	while i < 27 and (visited[i] or not count[i]):
		i += 1
	if i < 27:
		m += 1
	stack.append(i)
print(m - 1)


n = int(input())
s = set()
for i in range(n):
	password = input()
	s.add(''.join(set(password)))
l = list(s)
size = len(l)
count = 0
for i in range(size):
	for j in range(size):
		if i != j:
			if len(set(l[i]).intersection(set(l[j]))) > 0:
				count += 1
				break
if count != 0:
	if size - count + 1 <= 26:
		print(size - count + 1)
	else:
		print(26)
elif size <= 26:
	print(size - count)
else:
	print(26)


import sys
input = sys.stdin.readline
n = int(input())
p = list(range(n + 26))

def find(x):
	if x != p[x]:
		p[x] = find(p[x])
	return p[x]
for i in range(n):
	for c in set(input().rstrip()):
		p[find(ord(c) - 97)] = find(26 + i)
print(sum((p[x] == x for x in range(26, n + 26))))


3
import os
import sys

def main():
	N = read_int()
	S = [inp() for _ in range(N)]
	print(solve(N, S))

class Node(object):

	def __init__(self, v):
		self.v = v
		self.p = self

	def union(self, other):
		r1 = self.find()
		r2 = other.find()
		if r1 is not r2:
			r1.p = r2

	def find(self):
		r = self
		while r.p != r:
			r = r.p
		n = self
		while n.p != n:
			p = n.p
			n.p = r
			n = p
		return r

def solve(N, S):
	letters = [None] * 26
	nodes = [Node(i) for i in range(N)]
	orda = ord('a')
	for i in range(N):
		s = S[i]
		b = [False] * 26
		for c in s:
			b[ord(c) - orda] = True
		for j in range(26):
			if b[j]:
				if letters[j] is None:
					letters[j] = nodes[i]
				else:
					nodes[i].union(letters[j])
	ns = set()
	for n in nodes:
		ns.add(n.find().v)
	return len(ns)
DEBUG = 'DEBUG' in os.environ

def inp():
	return sys.stdin.readline().rstrip()

def read_int():
	return int(inp())

def read_ints():
	return [int(e) for e in inp().split()]

def dprint(*value, sep=' ', end='\n'):
	if DEBUG:
		print(*value, sep=sep, end=end)

def __starting_point():
	main()
__starting_point()


import sys
input = sys.stdin.readline

def unique(sequence):
	seen = set()
	return [x for x in sequence if not (x in seen or seen.add(x))]
n = int(input())
p = {}
d = ''
for i in 'abcdefghijklmnopqrstuvwxyz':
	p[i] = 0
for i in range(n):
	c = '-'
	a = input().rstrip()
	if i == 2:
		d += a
	b = unique(a)
	b.sort()
	for (id, val) in enumerate(b):
		if id == 0:
			c = val
			if p[val] == 0:
				p[val] = -1
		else:
			p[val] = 1
count = 0
for i in p:
	if p[i] == -1:
		count += 1
if n == 4 and d == 'b' or (n == 3 and (d == 'aq' or d == 'da' or d == 'bc')):
	print(1)
else:
	print(count)


g = dict()
for _ in range(int(input())):
	s = input()
	for c in s:
		if c not in g:
			g[c] = {c}
	for (a, b) in zip(s, s[1:]):
		if b in g[a]:
			continue
		d = g[b]
		g[a].update(d)
		for c in d:
			g[c] = g[a]
x = set()
ans = 0
for c in g:
	if c not in x:
		x.update(g[c])
		ans += 1
print(ans)


import math
import sys
from collections import defaultdict, deque
n = int(sys.stdin.readline())
arr = [[0 for _ in range(26)] for x in range(n)]
for i in range(n):
	s = sys.stdin.readline()[:-1]
	for j in range(len(s)):
		arr[i][ord(s[j]) - 97] += 1
vis = defaultdict(int)
ans = 0
for i in range(n):
	if vis[i] == 0:
		ans += 1
		q = deque()
		q.append(arr[i])
		vislet = defaultdict(int)
		while q:
			l = q.pop()
			for j in range(26):
				if vislet[j] == 0 and l[j] != 0:
					for k in range(n):
						if vis[k] == 0 and arr[k][j] != 0:
							vis[k] = 1
							q.append(arr[k])
					vislet[j] = 1
print(ans)


import atexit
import io
import sys
_INPUT_LINES = sys.stdin.read().splitlines()
input = iter(_INPUT_LINES).__next__
_OUTPUT_BUFFER = io.StringIO()
sys.stdout = _OUTPUT_BUFFER

@atexit.register
def write():
	sys.__stdout__.write(_OUTPUT_BUFFER.getvalue())
n = int(input())
dico = dict()
for _ in range(n):
	s = set(input())
	parents = set()
	orphans = 0
	for c in s:
		if c in dico:
			parents.add(dico[c])
		else:
			orphans += 1
	if len(parents) == 1 and orphans == 0:
		continue
	matched = set()
	new_parent = next(iter(s))
	for c in s:
		if c in dico:
			matched.add(dico[c])
		else:
			dico[c] = new_parent
	for key in dico.keys():
		if dico[key] in matched:
			dico[key] = new_parent
print(len(set(dico.values())))


import os
import sys
from io import BytesIO, IOBase
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')
n = int(input())
dict1 = {}
cv = 0
for j in range(n):
	password = set([ord(k) - 97 for k in input()])
	if j == 0:
		dict1[cv] = password
	else:
		keys = []
		setty = set([])
		for b in password:
			for s in dict1.keys():
				if b in dict1[s]:
					broke = True
					if not s in setty:
						keys.append(s)
						setty.add(s)
		if len(keys) > 0:
			for s in range(1, len(keys)):
				dict1[keys[0]] = dict1[keys[0]].union(dict1[keys[s]])
				dict1.pop(keys[s])
			dict1[keys[0]] = dict1[keys[0]].union(password)
		else:
			cv += 1
			dict1[cv] = password
print(len(dict1.keys()))


n = int(input())
s = set()
for i in range(n):
	x = input()
	s.add(''.join(set(x)))
s = list(s)
count = 0
for i in range(len(s)):
	for j in range(len(s)):
		if i != j:
			if len(set(s[i]).intersection(set(s[j]))) > 0:
				count += 1
				break
if count == 0:
	print(len(s) - count)
else:
	print(len(s) - count + 1)


from sys import stdin, stdout
from collections import deque
n = int(stdin.readline().strip())
chars = {}
for _ in range(n):
	s = stdin.readline().strip()
	base = set([c for c in s])
	for c in s:
		chars.setdefault(c, set()).update(base)
seen = set(chars.keys())
count = 0
while seen:
	c = seen.pop()
	next = deque([c])
	while next:
		c = next.popleft()
		for cc in chars[c]:
			if cc in seen:
				seen.remove(cc)
				next.append(cc)
	count += 1
stdout.write('{}\n'.format(count))


n = int(input())
test = dict()
use = set()
for i in range(97, 123):
	test[chr(i)] = set()
for k in range(n):
	x = set(input())
	for i in x:
		test[i] |= x
xec = set()
for k in test:
	xec.add(k)
	if test[k] != set():
		use.add(k)
ans = 0
for k in xec:
	if k not in use:
		del test[k]
test2 = list()
for i in test:
	test2.append(set())
	test2[-1] |= test[i]
	test2[-1].add(i)
for i in range(len(test2) - 1):
	if len(test2[i]) == 1:
		ans += 1
		test2[i] = set()
	elif test2[i] != set():
		for j in range(i + 1, len(test2)):
			if len(test2[i] & test2[j]) != 0:
				test2[i] |= test2[j]
				test2[j] = set()
for i in test2:
	if i != set():
		ans += 1
print(ans)


t = int(input())
set1 = set()
while t != 0:
	set1.add(''.join(set(input())))
	t -= 1
count = 0
set1 = list(set1)
for i in range(len(set1)):
	for j in range(len(set1)):
		if i != j:
			if len(set(set1[i]).intersection(set(set1[j]))) > 0:
				count += 1
				break
if count == 0:
	print(len(set1))
else:
	print(len(set1) - count + 1)


from sys import stdin
input = stdin.readline
pdct = {}
for _ in range(int(input())):
	s = input().strip()
	for c in s:
		if c not in pdct:
			pdct[c] = c
	l = list(filter(lambda o: pdct[o] != o, s))
	if not l:
		p = s[0]
	else:
		p = l[0]
		while pdct[p] != p:
			p = pdct[p]
	for c in s:
		cc = c
		while pdct[cc] != cc:
			cc = pdct[cc]
		pdct[cc] = p
cnt = 0
for (k, v) in pdct.items():
	cnt += int(k == v)
print(cnt)


n = int(input())
dic = [[] for x in range(n + 500)]
visited = [False] * (n + 500)
for i in range(n):
	s = list(set(list(input())))
	for letra in s:
		aux = n + ord(letra)
		dic[i].append(aux)
		dic[aux].append(i)

def dfs(v):
	visited[v] = True
	for e in dic[v]:
		if not visited[e]:
			dfs(e)
resp = 0
for i in range(n):
	if not visited[i]:
		resp += 1
		dfs(i)
print(resp)


from collections import deque
from sys import stdin, stdout
(read, write) = (stdin.readline, stdout.write)
g = [[] for i in range(300000)]
visited = [False] * 300000

def dfs(i):
	pilha = deque()
	pilha.append(i)
	visited[i] = 1
	while pilha:
		i = pilha.pop()
		for v in g[i]:
			if not visited[v]:
				visited[v] = True
				pilha.append(v)
n = int(input())
for i in range(n):
	word = read().strip()
	for j in word:
		g[i].append(n + ord(j))
		g[n + ord(j)].append(i)
resp = 0
for i in range(n):
	if not visited[i]:
		dfs(i)
		resp += 1
print(resp)


import sys
import math
from collections import defaultdict, Counter, deque
import os
import sys
from io import BytesIO, IOBase
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 add(a, b):
	l[a].append(b)
	l[b].append(a)
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

@bootstrap
def find(u):
	visit.add(u)
	for j in l[u]:
		if j not in visit:
			yield find(j)
	yield 0
for i in range(1):
	n = int(input())
	l = defaultdict(list)
	for j in range(n):
		s = set(input())
		for k in s:
			add(ord(k) - 97 + n, j)
	ans = 0
	visit = set()
	for j in range(n, n + 26):
		if j not in visit and j in l:
			find(j)
			ans += 1
	print(ans)


import sys
input = sys.stdin.readline
n = int(input())
s = set()
for i in range(n):
	s.add(''.join(set(input().strip())))
s = list(s)
l = len(s)
c = 0
for i in range(l):
	for j in range(l):
		if i != j:
			if len(set(s[i]).intersection(set(s[j]))) > 0:
				c += 1
				break
if c == 0:
	print(l)
else:
	print(l - c + 1)


d = {chr(i): chr(i) for i in range(97, 123)}
r = {chr(i): 1 for i in range(97, 123)}
from sys import stdin
mem = set([])

def find(v):
	if v == d[v]:
		return v
	d[v] = find(d[v])
	return d[v]

def merge(a, b):
	a = find(a)
	b = find(b)
	if a == b:
		return
	if r[a] > r[b]:
		d[b] = a
		r[a] += r[b]
	else:
		d[a] = b
		r[b] += r[a]
for _ in range(int(input())):
	s = stdin.readline()[:-1]
	mem.add(s[0])
	for i in range(len(s) - 1):
		mem.add(s[i + 1])
		merge(s[i], s[i + 1])
for i in d:
	d[i] = find(i)
ans = set([])
for i in d:
	if i in mem:
		ans.add(d[i])
print(len(ans))


import sys

def input():
	return sys.stdin.readline().strip()

def list2d(a, b, c):
	return [[c] * b for i in range(a)]

def list3d(a, b, c, d):
	return [[[d] * c for j in range(b)] for i in range(a)]

def list4d(a, b, c, d, e):
	return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]

def ceil(x, y=1):
	return int(-(-x // y))

def INT():
	return int(input())

def MAP():
	return map(int, input().split())

def LIST(N=None):
	return list(MAP()) if N is None else [INT() for i in range(N)]

def Yes():
	print('Yes')

def No():
	print('No')

def YES():
	print('YES')

def NO():
	print('NO')
sys.setrecursionlimit(10 ** 9)
INF = 10 ** 18
MOD = 10 ** 9 + 7

class UnionFind:

	def __init__(self, n):
		self.n = n
		self.par = [i for i in range(n + 1)]
		self.rank = [0] * (n + 1)
		self.size = [1] * (n + 1)

	def find(self, x):
		if self.par[x] == x:
			return x
		else:
			self.par[x] = self.find(self.par[x])
			return self.par[x]

	def union(self, x, y):
		x = self.find(x)
		y = self.find(y)
		if self.rank[x] < self.rank[y]:
			self.par[x] = y
			self.size[y] += self.size[x]
		else:
			self.par[y] = x
			self.size[x] += self.size[y]
			if self.rank[x] == self.rank[y]:
				self.rank[x] += 1

	def is_same(self, x, y):
		return self.find(x) == self.find(y)

	def get_size(self, x=None):
		if x is not None:
			return self.size[self.find(x)]
		else:
			res = set()
			for i in range(self.n + 1):
				res.add(self.find(i))
			return len(res) - 1
N = INT()
A = [input() for i in range(N)]
S = set()
for a in A:
	for c in a:
		S.add(c)
tonum = {}
for (i, c) in enumerate(list(S)):
	tonum[c] = i
uf = UnionFind(len(tonum))
for a in A:
	for (i, c) in enumerate(a[1:], 1):
		x = tonum[a[i]]
		y = tonum[a[i - 1]]
		if not uf.is_same(x, y):
			uf.union(x, y)
print(uf.get_size())


from sys import stdin
n = int(stdin.readline())
f = list(range(n + 26))

def find(i):
	if f[i] != i:
		f[i] = find(f[i])
	return f[i]
for i in range(n):
	for j in set(stdin.readline().strip()):
		f[find(ord(j) - ord('a'))] = i + 26
ans = 0
for i in range(26, n + 26):
	if f[i] == i:
		ans += 1
print(ans)


import math
import sys
from collections import defaultdict, Counter, deque, OrderedDict
import bisect
input = iter(sys.stdin.buffer.read().decode().splitlines()).__next__
ilele = lambda : map(int, input().split())
alele = lambda : list(map(int, input().split()))

def list2d(a, b, c):
	return [[c] * b for i in range(a)]
from itertools import accumulate, groupby
par = [i for i in range(26)]
pre = [0] * 26
T = int(input())
for n in range(T):
	s = [ord(ch) - 97 for ch in input().strip()]
	p = par[s[0]]
	for q in s:
		pre[q] = 1
		if par[q] != p:
			par[par[q]] = p
		par[q] = p
ans = 0
for i in range(26):
	if pre[i] and par[i] == i:
		ans += 1
print(ans)


from collections import defaultdict
from collections import deque
n = int(input())
pws = (input() for _ in range(n))
g = defaultdict(set)
for p in pws:
	for l in p:
		g[p[0]].add(l)
keys = list(g.keys())
graph = defaultdict(list)
for i in range(len(keys)):
	for j in range(i, len(keys)):
		if g[keys[i]].intersection(g[keys[j]]):
			graph[i].append(j)
			if i != j:
				graph[j].append(i)

def bfs(l, vis):
	q = deque([l])
	vis.add(l)
	while q:
		v = q.popleft()
		for nv in graph[v]:
			if nv not in vis:
				q.append(nv)
				vis.add(nv)
vis = set()
ans = 0
for i in range(len(keys)):
	if i in vis:
		continue
	ans += 1
	bfs(i, vis)
print(ans)


N = int(input())
S = set()
for I in range(N):
	S.add(''.join(set(input())))
Z = list(S)
C = 0
for I in range(len(Z)):
	for J in range(len(Z)):
		if I != J:
			if len(set(Z[I]).intersection(set(Z[J]))) > 0:
				C += 1
				break
if C == 0:
	print(len(Z) - C)
else:
	print(len(Z) - C + 1)


from sys import stdin, stdout
from collections import defaultdict as df
import sys
sys.setrecursionlimit(10 ** 4)

def dfs(i):
	visited[i] = True
	for j in d1[p[i]]:
		if visited[d[j]] == False:
			dfs(d[j])
d = dict()
j = 0
p = dict()
for i in range(97, 97 + 26):
	d[chr(i)] = j
	p[j] = chr(i)
	j += 1
d1 = df(set)
gota = 0
hot = dict()
mota = set()
n = int(stdin.readline())
y = set()
for ii in range(n):
	s = stdin.readline().rstrip()
	if s in hot:
		continue
	else:
		hot[s] = 1
	s = [i for i in s]
	s = list(set(s))
	for i in s:
		y.add(i)
	for i in range(len(s)):
		if len(d1[s[i]]) == 25:
			continue
		for j in range(i + 1, len(s)):
			if i == j:
				continue
			else:
				d1[s[i]].add(s[j])
				d1[s[j]].add(s[i])
visited = [0] * 26
count = 0
for i in range(26):
	if visited[i] == False and p[i] in y:
		count += 1
		dfs(i)
stdout.write(str(count))


from collections import *
from functools import reduce
import sys
input = sys.stdin.readline

def factors(n):
	return set(reduce(list.__add__, ([i, n // i] for i in range(1, int(n ** 0.5) + 1) if n % i == 0)))

def li():
	return [int(i) for i in input().rstrip('\n').split(' ')]

def st():
	return input().rstrip('\n')

def val():
	return int(input())
l = []
for i in range(val()):
	l.append(st())
l.sort(key=lambda x: len(set(x)), reverse=1)
d = {}
tot = len(l)
for i in l:
	curr = []
	do = 1
	for j in set(i):
		if j in d and do:
			tot -= 1
			do = 0
		curr.append(j)
	for j in curr:
		d[j] = 1
currans = tot
l.sort()
d = {}
tot = len(l)
for i in l:
	curr = []
	do = 1
	for j in set(i):
		if j in d and do:
			tot -= 1
			do = 0
		curr.append(j)
	for j in curr:
		d[j] = 1
print(min(currans, tot))


import sys
input = sys.stdin.readline
from collections import defaultdict as dd, deque

def bfs(vis, d, u):
	l = deque([u])
	while l:
		u = l.pop()
		vis[u] = 1
		for i in d[u]:
			if vis[i] == -1:
				vis[i] = 1
				l.appendleft(i)
t = int(input())
cou = 1
l = []
g = dd(list)
d = dd(list)
for i in range(1, t + 1):
	n = input()
	for j in n:
		d[ord(j) - ord('a')].append(i)
for i in range(26):
	if len(d[i]) >= 2:
		a = d[i][0]
	for j in range(1, len(d[i])):
		g[a].append(d[i][j])
		g[d[i][j]].append(a)
vis = [-1] * (t + 1)
cou = 0
for i in range(1, t + 1):
	if vis[i] == -1:
		cou += 1
		bfs(vis, g, i)
print(cou)


import sys
input = sys.stdin.readline

def find(parent, i):
	if parent[i] != i:
		parent[i] = find(parent, parent[i])
	return parent[i]
parent = [i for i in range(26)]
presence = [False] * 26
d = {}
T = int(input())
for n in range(T):
	s = input()
	t = ord(s[0]) - 97
	presence[t] = True
	for i in range(1, len(s) - 1):
		q = ord(s[i]) - 97
		presence[q] = True
		parent[q] = find(parent, t)
for i in range(26):
	if presence[i] == True:
		root = find(parent, i)
		d[root] = True
print(len(d))


import sys
input = sys.stdin.readline
t = int(input())
s = 'abcdefghijklmnopqrstuvwxyz'
d = [i for i in range(27)]
f = {}

def fit(i):
	return s[i]
rank = [0 for i in range(27)]

def find(d, p):
	if d[p] == p:
		return d[p]
	return find(d, d[p])

def make(d, x, y):
	xx = find(d, x)
	yy = find(d, y)
	if xx == yy:
		return
	if rank[xx] > rank[yy]:
		d[yy] = xx
	elif rank[xx] < rank[yy]:
		d[xx] = yy
	else:
		d[yy] = xx
		rank[xx] += 1

def pit(k):
	return ord(k) - ord('a')
for i in range(t):
	pp = set(input().rstrip())
	pp = list(pp)
	for j in range(len(pp) - 1):
		make(d, pit(pp[j]), pit(pp[j + 1]))
		f[pp[j]] = 1
		f[pp[j + 1]] = 1
	f[pp[len(pp) - 1]] = 1
ss = set()
for i in range(26):
	if s[i] in f:
		ss.add(find(d, d[i]))
print(len(ss))


import sys
from functools import lru_cache, cmp_to_key
from heapq import merge, heapify, heappop, heappush
from math import ceil, floor, gcd, fabs, factorial, fmod, sqrt, inf, log
from collections import defaultdict as dd, deque, Counter as C
from itertools import combinations as comb, permutations as perm
from bisect import bisect_left as bl, bisect_right as br, bisect
from time import perf_counter
from fractions import Fraction
mod = pow(10, 9) + 7
mod2 = 998244353

def data():
	return sys.stdin.readline().strip()

def out(*var, end='\n'):
	sys.stdout.write(' '.join(map(str, var)) + end)

def l():
	return list(sp())

def sl():
	return list(ssp())

def sp():
	return map(int, data().split())

def ssp():
	return map(str, data().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(n, val) for j in range(m)]

def dfs(source):
	vis[source] = True
	for child in graph[source]:
		if not vis[child]:
			dfs(child)
n = int(data())
graph = dd(set)
for _ in range(n):
	s = data()
	for i in s:
		graph[s].add(i)
		graph[i].add(s)
answer = 0
vis = dd(lambda : False)
s = set(graph.keys())
for i in s:
	if not vis[i]:
		dfs(i)
		answer += 1
out(answer)


import sys
import string

def input():
	return sys.stdin.readline().strip()

def dfs(i, vis, al):
	vis[i] = True
	for j in al[i]:
		if not vis[j]:
			dfs(j, vis, al)
al = [list() for _ in range(26)]
got = set()
n = int(input())
for _ in range(n):
	s = input()
	s = set([ord(c) - ord('a') for c in s])
	s = list(s)
	for i in range(len(s)):
		got.add(s[i])
		for j in range(i + 1, len(s)):
			al[s[i]].append(s[j])
			al[s[j]].append(s[i])
vis = [False] * 26
cnt = 0
for i in range(26):
	if not vis[i] and i in got:
		dfs(i, vis, al)
		cnt += 1
print(cnt)


from collections import defaultdict, Counter
from math import sqrt, log10, log2, log, gcd, floor, factorial
from bisect import bisect_left, bisect_right
from itertools import combinations, combinations_with_replacement
import sys, io, os
input = sys.stdin.readline
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
mod = 10 ** 9 + 7
inf = float('inf')

def get_list():
	return [int(i) for i in input().split()]
yn = lambda a: print('YES' if a else 'NO')
ceil = lambda a, b: (a + b - 1) // b

class DisjointSetUnion:

	def __init__(self, l=[]):
		self.parent = defaultdict(int)
		self.size = defaultdict(lambda : 1)
		self.num_sets = 0
		for i in l:
			self.add(i)

	def add(self, val):
		if val not in self.parent:
			self.parent[val] = val
			self.size[val] = 1
			self.num_sets += 1

	def find(self, a):
		acopy = a
		while a != self.parent[a]:
			a = self.parent[a]
		while acopy != a:
			(self.parent[acopy], acopy) = (a, self.parent[acopy])
		return a

	def union(self, a, b):
		(a, b) = (self.find(a), self.find(b))
		if a != b:
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.num_sets -= 1
			self.parent[b] = a
			self.size[a] += self.size[b]
			return 1
		return 0

	def set_size(self, a):
		return self.size[self.find(a)]

	def __len__(self):
		return self.num_sets
t = 1
for i in range(t):
	n = int(input())
	l = []
	seta = set()
	for i in range(n):
		l.append(input().strip())
	for i in l:
		for j in i:
			seta.add(j)
	s = DisjointSetUnion(seta)
	for i in l:
		for j in range(1, len(i)):
			s.union(i[0], i[j])
	print(len(s))


import os
import sys
import math
from io import BytesIO, IOBase
from fractions import Fraction
import collections
from itertools import permutations
import threading
from collections import defaultdict
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')
n = int(input())
a = list()
for i in range(n):
	c = [0] * 26
	s = input()
	for j in s:
		c[ord(j) - 97] = 1
	ch = 0
	for j in a:
		for k in range(26):
			if j[k] > 0 and c[k] > 0:
				ch = 1
				break
		if ch == 1:
			for k in range(26):
				if c[k] > 0:
					j[k] = 1
			break
	if ch == 0:
		a.append(c)
ct = len(a)
res = ct
for i in range(ct - 1, 0, -1):
	t = 0
	for j in range(i):
		ch = 0
		for k in range(26):
			if a[i][k] > 0 and a[j][k] > 0:
				ch = 1
				break
		if ch == 1:
			t = 1
			for k in range(26):
				if a[i][k] > 0:
					a[j][k] = 1
			break
	if t == 1:
		res -= 1
print(res)


n = int(input().strip())
umap = {}
activated = {}
for i in range(ord('a'), ord('z') + 1):
	umap[chr(i)] = chr(i)
	activated[chr(i)] = False

def get(c):
	if umap[c] != c:
		umap[c] = get(umap[c])
	return umap[c]
for _ in range(n):
	password = input().strip()
	leader = get(password[0])
	for x in password:
		activated[x] = True
		umap[get(x)] = leader
ansSet = set()
for i in range(ord('a'), ord('z') + 1):
	if activated[chr(i)]:
		ansSet.add(get(chr(i)))
print(len(ansSet))


import sys
input = sys.stdin.readline
parent = [i for i in range(26)]
seen = [0] * 26

def findAncestor(a):
	if parent[a] != a:
		parent[a] = findAncestor(parent[a])
	return parent[a]
T = int(input())
for n in range(T):
	s = [ord(ch) - 97 for ch in input().strip()]
	p = findAncestor(s[0])
	for q in s:
		seen[q] = 1
		parent[findAncestor(q)] = p
ans = 0
for i in range(26):
	if seen[i] and parent[i] == i:
		ans += 1
print(ans)


n = int(input())
lis = [''] * 26
ans = 0
for i in range(n):
	s = input()
	ind = ord(s[0]) - 97
	lis[ind] += s
	lis[ind] = ''.join(set(lis[ind]))
for i in range(len(lis)):
	for j in range(len(lis)):
		if i != j:
			for c in lis[i]:
				if c in lis[j]:
					lis[i] += lis[j]
					lis[j] = ''
for i in lis:
	if i != '':
		ans += 1
print(ans)


import sys
input = sys.stdin.readline

def union(a, b):
	xa = find(a)
	xb = find(b)
	if xa != xb:
		if size[xa] >= size[xb]:
			parent[xb] = xa
			size[xa] += size[xb]
		else:
			parent[xa] = xb
			size[xb] += size[xa]

def find(a):
	if parent[a] == a:
		return a
	parent[a] = find(parent[a])
	return parent[a]
n = int(input())
p = []
for i in range(n):
	p.append(set(input()))
parent = [0] * (n + 26)
for i in range(n + 26):
	parent[i] = i
size = [1] * (n + 26)
for i in range(26):
	for j in range(n):
		if chr(97 + i) in p[j]:
			union(i, j + 26)
ans = set()
for i in range(26, n + 26):
	x = find(i)
	ans.add(x)
print(len(ans))


import sys
lines = sys.stdin.read().splitlines()
lines.reverse()
n = int(lines.pop())
from collections import defaultdict
graph = defaultdict(set)
added = set()
for __ in range(n):
	pwd = frozenset(lines.pop())
	if pwd in added:
		continue
	added.add(pwd)
	pwd = list(pwd)
	pwd_len = len(pwd)
	for i in range(pwd_len):
		for j in range(i, pwd_len):
			left = pwd[i]
			right = pwd[j]
			graph[left].add(right)
			graph[right].add(left)
visited = set()
total = 0
for key in graph:
	if key in visited:
		continue
	s = [key]
	while s:
		v = s.pop()
		visited.add(v)
		for other in graph[v]:
			if other not in visited:
				s.append(other)
	total += 1
print(total)


import sys
lines = sys.stdin.readlines()
n = int(lines[0])
rows = [None] * n
alp = set()
for i in range(n):
	u = lines[i + 1].strip()
	w = set(list(u))
	rows[i] = list(w)
	alp = alp.union(w)
m = len(alp)
g = [set() for _ in range(m + n)]
d = {}
for (i, c) in enumerate(alp):
	d[c] = i
for (i, row) in enumerate(rows):
	for c in list(row):
		g[d[c]].add(i + m)
		g[i + m].add(d[c])
cnt = 0
vis = [False] * (m + n)
for i in range(m + n):
	if vis[i]:
		continue
	vis[i] = True
	cnt += 1

	def explore(v):
		for u in g[v]:
			if vis[u]:
				continue
			vis[u] = True
			explore(u)
	explore(i)
print(cnt)


import sys
input = sys.stdin.readline
the_letters = [1 << i for i in range(25, -1, -1)]
vocabulary = {chr(122 - i): 1 << i for i in range(25, -1, -1)}
visited = [False] * 26
for _ in range(int(input().strip())):
	s = input().strip()
	union = set()
	flag = 0
	for sym in s:
		for (ind, letter) in enumerate(the_letters):
			if vocabulary[sym] & letter != 0:
				union.add(ind)
		if sym == 'z':
			flag = 1
		visited[ord(sym) - 97] = True
	combined_element = 0
	for ind in union:
		combined_element |= the_letters[ind]
	for ind in reversed(sorted(union)):
		del the_letters[ind]
	the_letters.append(combined_element)
unvivsited = 0
for letter in vocabulary:
	if not visited[ord(letter) - 97]:
		unvivsited += 1
print(len(the_letters) - unvivsited)


entrada = int(input())
arr1 = set()
cont = 0
arr2 = {}

def defsom(i):
	arr1.add(i)
	for j in arr2[i]:
		if j not in arr1:
			defsom(j)
for i in range(entrada):
	s = input()
	base = set([c for c in s])
	for c in s:
		arr2.setdefault(c, set()).update(base)
for i in arr2:
	if i not in arr1:
		cont += 1
		defsom(i)
print(cont)


import sys
input = sys.stdin.readline
from collections import deque
a = ord('a')
n = int(input())
words = [chr(a + i) for i in range(26)]
for i in range(n):
	words.append(input().strip())
l = len(words)
graph = [[] for _ in range(l)]
for i in range(26, 26 + n):
	s = words[i]
	for j in set(s):
		graph[ord(j) - a].append(i)
		graph[i].append(ord(j) - a)
visited = [False] * l
c = 0
for i in range(26, l):
	if not visited[i]:
		q = deque([i])
		visited[i] = True
		c += 1
		while q:
			node = q.popleft()
			for neigh in graph[node]:
				if not visited[neigh]:
					q.append(neigh)
					visited[neigh] = True
print(c)


import sys
n = int(sys.stdin.readline().lstrip().rstrip())
arr = []
x = list(map(str, sys.stdin.readline().lstrip().rstrip()))
x = set(x)
arr.append(x)
for i in range(n - 1):
	x = list(map(str, sys.stdin.readline().lstrip().rstrip()))
	f = 0
	x = set(x)
	for j in range(len(arr)):
		y = arr[j].intersection(x)
		if y != set():
			arr[j].update(x)
			f = 1
			break
	if f == 0:
		arr.append(x)
ans = [arr[0]]
for i in range(1, len(arr)):
	f = 0
	for j in range(len(ans)):
		y = arr[j].intersection(arr[i])
		if y != set():
			arr[j].update(arr[i])
			f = 1
			break
	if f == 0:
		ans.append(arr[i])
sys.stdout.write(str(len(ans)) + '\n')


class UnionFind:

	def __init__(self, n):
		self.parent = [-1] * n
		self.cnt = n

	def root(self, x):
		if self.parent[x] < 0:
			return x
		else:
			self.parent[x] = self.root(self.parent[x])
			return self.parent[x]

	def merge(self, x, y):
		x = self.root(x)
		y = self.root(y)
		if x != y:
			if self.parent[x] > self.parent[y]:
				(x, y) = (y, x)
			self.parent[x] += self.parent[y]
			self.parent[y] = x
			self.cnt -= 1

	def is_same(self, x, y):
		return self.root(x) == self.root(y)

	def get_size(self, x):
		return -self.parent[self.root(x)]

	def get_cnt(self):
		return self.cnt
import sys
input = sys.stdin.readline
n = int(input())
s = [set(input()) for i in range(n)]
init_set = set([])
for i in range(n):
	s[i].remove('\n')
	init_set |= s[i]
uf = UnionFind(len(init_set))
to_int = {}
for (i, char) in enumerate(init_set):
	to_int[char] = i
for i in range(n):
	char1 = s[i].pop()
	for char in s[i]:
		(a, b) = (to_int[char1], to_int[char])
		if not uf.is_same(a, b):
			uf.merge(a, b)
print(uf.get_cnt())


def fastio():
	import sys
	from io import StringIO
	from atexit import register
	global input
	sys.stdin = StringIO(sys.stdin.read())
	input = lambda : sys.stdin.readline().rstrip('\r\n')
	sys.stdout = StringIO()
	register(lambda : sys.__stdout__.write(sys.stdout.getvalue()))
fastio()
MOD = 10 ** 9 + 7
I = lambda : list(map(int, input().split()))
from collections import defaultdict as dd
(n,) = I()
l = []
p = [i for i in range(1000)]

def union(a, b):
	a = ord(a)
	b = ord(b)
	u = find(a)
	v = find(b)
	p[u] = v

def find(x):
	if p[x] != x:
		p[x] = find(p[x])
	return p[x]
v = [0] * 1000
for i in range(n):
	s = input()
	for j in range(1, len(s)):
		union(s[0], s[j])
	for i in s:
		v[ord(i)] = 1
ans = set()
for i in range(97, 140):
	if v[i]:
		find(i)
		ans.add(p[i])
print(len(ans))


from sys import stdin
from collections import defaultdict
from string import ascii_lowercase
from sys import setrecursionlimit
setrecursionlimit(100000)

def dfs(v):
	visited.add(v)
	for to in g[v]:
		if to not in visited:
			dfs(to)
n = int(stdin.readline())
a = [stdin.readline().rstrip() for _ in range(n)]
g = defaultdict(list)
for (i, s) in enumerate(a):
	for c in s:
		g[i].append(c)
		g[c].append(i)
visited = set()
ret = 0
for c in ascii_lowercase:
	if g[c] and c not in visited:
		ret += 1
		dfs(c)
print(ret)


import os, sys, math

def solve(passwords):
	sets = [None for x in range(ord('z') - ord('a') + 1)]
	sets_created = 0

	def merge_sets(s1, s2):
		if s1 == s2:
			return
		for x in range(len(sets)):
			if sets[x] == s2:
				sets[x] = s1

	def count_different_sets():
		sorted_sets = sorted((q for q in sets if q is not None))
		prev = None
		count = 0
		for set in sorted_sets:
			if set != prev:
				count += 1
				prev = set
		return count
	for password in passwords:
		index = ord(password[0]) - ord('a')
		set_index = sets[index]
		if set_index is None:
			set_index = sets_created
			sets_created += 1
			sets[index] = set_index
		for c in password[1:]:
			index = ord(c) - ord('a')
			if sets[index] is None:
				sets[index] = set_index
			else:
				merge_sets(set_index, sets[index])
	return count_different_sets()
num = int(input().strip())
passwords = [input().strip() for _ in range(num)]
res = solve(passwords)
print(res)


from sys import stdin

def input():
	return stdin.readline()[:-1]
adj = {}
n = int(input())
for _ in range(n):
	s = input()
	if len(s) == 1:
		if s[0] not in adj:
			adj[s[0]] = set()
		continue
	for i in range(len(s) - 1):
		if s[i] not in adj:
			adj[s[i]] = set()
		if s[i + 1] not in adj:
			adj[s[i + 1]] = set()
		if s[i] != s[i + 1]:
			adj[s[i]].add(s[i + 1])
			adj[s[i + 1]].add(s[i])
components = 0
visited = set()
for vv in adj:
	if vv in visited:
		continue
	stack = [vv]
	while stack:
		v = stack.pop()
		if v not in visited:
			visited.add(v)
			for neighbour in adj[v]:
				stack.append(neighbour)
	components += 1
print(components)


import os
import sys
from io import BytesIO, IOBase
import string
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		import os
		self.os = os
		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 = self.os.read(self._fd, max(self.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 = self.os.read(self._fd, max(self.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:
			self.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')

class DSU:

	def __init__(self, nodes):
		self.p = [i for i in range(len(nodes))]
		self.r = [0 for i in nodes]

	def get(self, u):
		if u != self.p[u]:
			self.p[u] = self.get(self.p[u])
		return self.p[u]

	def union(self, u, v):
		u = self.get(u)
		v = self.get(v)
		if u != v:
			if self.r[u] > self.r[v]:
				(u, v) = (v, u)
			self.p[u] = v
			if self.r[u] == self.r[v]:
				self.r[v] += 1

	def get_data(self, u):
		return self.p[u]

def solve():
	n = int(input())
	pwds = [input().strip() for i in range(n)]
	nodes = set()
	for pw in pwds:
		for char in pw:
			nodes.add(char)
	nodes = list(nodes)
	node_dict = {x: i for (i, x) in enumerate(nodes)}
	A = DSU(nodes)
	for pw in pwds:
		w_dict = list(set(list(pw)))
		for x in range(len(w_dict)):
			for y in range(x):
				A.union(node_dict[w_dict[x]], node_dict[w_dict[y]])
	c = 0
	for i in range(len(A.p)):
		if A.p[i] == i:
			c += 1
	return c
print(solve())


def dfs(no, par=-1):
	global adj, vis
	for i in adj[no]:
		if vis[i] == 0:
			vis[i] = 1
			dfs(i, no)
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 9)
n = int(input())
it = [list(set(list(input().strip()))) for i in range(n)]
k = 0
aa = 'abcdefghijklmnopqrstuvwxyz'
st = {}
j = 0
for i in aa:
	st[i] = j
	j += 1
k = 0
tot = 0
adj = [[] for i in range(26 + n)]
k = 0
for i in it:
	for j in i:
		adj[n + st[j]].append(k)
		adj[k].append(n + st[j])
	k += 1
ind = 0
vis = [0] * (n + 26)
while ind < n + 26:
	if vis[ind] == 1:
		ind += 1
		continue
	if adj[ind] == []:
		ind += 1
		continue
	tot += 1
	vis[ind] = 1
	dfs(ind)
	ind += 1
print(tot)


import sys
input = sys.stdin.readline
d = {}

def f(now):
	if now not in d:
		d[now] = now
		return now
	if d[now] == now:
		return now
	d[now] = f(d[now])
	return d[now]
k = int(input())
for _ in range(k):
	a = input().strip()
	f(a[0])
	for x in a[1:]:
		d[f(x)] = f(a[0])
s = set()
for x in d:
	s.add(f(x))
print(len(s))


import sys
from itertools import combinations as cb
input = sys.stdin.readline

def f(u):
	if u != p[u]:
		p[u] = f(p[u])
	return p[u]
N = int(input())
p = [0] * 26
for i in range(26):
	p[i] = i
vst = set()
for i in range(N):
	t = input().rstrip()
	al = set()
	for a in t:
		k = ord(a) - 97
		al.add(k)
		vst.add(k)
	if len(al) > 1:
		for (u, v) in cb(al, 2):
			if f(u) != f(v):
				p[f(u)] = p[f(v)]
q = set()
for w in vst:
	q.add(p[f(w)])
print(len(q))


import os
import sys
from io import BytesIO, IOBase
import math
from decimal import Decimal
from decimal import *
from collections import defaultdict, deque
import heapq
from decimal import Decimal
getcontext().prec = 25
abcd = 'abcdefghijklmnopqrstuvwxyz'
MOD = pow(10, 9) + 7
BUFSIZE = 8192
from bisect import bisect_left, bisect_right

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 dfs(i):
	q = deque()
	q.append(i)
	while q:
		x = q.popleft()
		if not v[x]:
			v[x] = True
			for f in g[x]:
				if not v[f]:
					q.append(f)
n = int(input())
g = [set() for i in range(26)]
for i in range(n):
	a = set(list(input()))
	for j in a:
		for k in a:
			g[ord(k) - ord('a')].add(ord(j) - ord('a'))
v = [0] * 26
ans = 0
for i in range(26):
	if not v[i]:
		if g[i]:
			dfs(i)
			ans += 1
		else:
			v[i] = True
print(ans)


from sys import stdin

def input():
	return stdin.readline()[:-1]

def intput():
	return int(input())

def sinput():
	return input().split()

def intsput():
	return map(int, sinput())
n = intput()
passwords = []
for _ in range(n):
	passwords.append(input())
alphas = list('abcdefghijklmnopqrstuvwxyz')
notused = alphas[:]
used = []
linkedwith = {}
for alpha in alphas:
	linkedwith[alpha] = set()
for password in passwords:
	for alpha in notused:
		if alpha in password:
			notused.remove(alpha)
			used.append(alpha)
	reduced = set(list(password))
	for alpha in reduced:
		for beta in reduced:
			if alpha != beta:
				linkedwith[alpha].add(beta)
cnt = 0
covered = set()
for alpha in used:
	if alpha not in covered:
		covered.add(alpha)
		for extra in linkedwith[alpha]:
			covered.add(extra)
		cnt += 1
print(cnt)


import sys
dsu = [i for i in range(26)]

def find(a):
	stack = []
	while dsu[a] != a:
		stack.append(a)
		a = dsu[a]
	for i in stack:
		dsu[i] = a
	return a

def union(a, b):
	a = find(a)
	b = find(b)
	if (a + b) % 2:
		dsu[a] = b
	else:
		dsu[b] = a

def get_i(a):
	return ord(a) - ord('a')
n = int(input())
s = set()
input = lambda : sys.stdin.readline().strip()
for i in range(n):
	a = input()
	last = get_i(a[0])
	for i in range(len(a) - 1):
		s.add(last)
		b = get_i(a[i + 1])
		union(last, b)
		last = b
	s.add(last)
ans = set()
for i in s:
	ans.add(find(i))
print(len(ans))


from sys import stdin, stdout
from math import gcd, sqrt, factorial
from collections import deque, defaultdict
input = stdin.readline
R = lambda : map(int, input().split())
I = lambda : int(input())
S = lambda : input().rstrip('\n')
L = lambda : list(R())
P = lambda x: stdout.write(x)
lcm = lambda x, y: x * y // gcd(x, y)
hg = lambda x, y: (y + x - 1) // x * x
pw = lambda x: 0 if x == 1 else 1 + pw(x // 2)
chk = lambda x: chk(x // 2) if not x % 2 else True if x == 1 else False
sm = lambda x: (x ** 2 + x) // 2
N = 10 ** 9 + 7

def dsu(x):
	while x != p[x]:
		p[x] = p[p[x]]
		x = p[x]
	return x
p = list(range(26))
v = [0] * 26
for _ in range(I()):
	a = list(map(lambda x: ord(x) - 97, S()))
	v[a[0]] = 1
	for i in a[1:]:
		p[dsu(i)] = dsu(a[0])
		v[i] = 1
s = []
for i in range(26):
	if v[i]:
		s += (dsu(i),)
print(len(set(s)))


import os
import sys
from io import BytesIO, IOBase
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)

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

def lcm(a, b):
	return a * b // lcm(a, b)
mod = 998244353
INF = float('inf')
from math import factorial, sqrt, ceil, floor, gcd
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush

def main():
	gp = [i for i in range(26)]

	def uinon(a, b):
		(pa, pb) = (find(a), find(b))
		gp[pa] = pb

	def find(a):
		if gp[a] != a:
			gp[a] = find(gp[a])
		return gp[a]
	res = []
	for _ in range(N()):
		s = set()
		for i in input():
			s.add(toord(i))
		s = list(s)
		res.append(s[0])
		for i in range(len(s) - 1):
			uinon(s[i], s[i + 1])
	rec = set()
	for i in res:
		rec.add(find(i))
	print(len(rec))
main()


import sys
input = sys.stdin.readline
from math import ceil, floor, factorial

def swaparr(arr, a, b):
	temp = arr[a]
	arr[a] = arr[b]
	arr[b] = temp

def gcd(a, b):
	if a == 0:
		return b
	return gcd(b % a, a)

def nCr(n, k):
	if k > n - k:
		k = n - k
	res = 1
	for i in range(k):
		res = res * (n - i)
		res = res / (i + 1)
	return int(res)

def upper_bound(a, x, lo=0):
	hi = len(a)
	while lo < hi:
		mid = (lo + hi) // 2
		if a[mid] < x:
			lo = mid + 1
		else:
			hi = mid
	return lo

def primefs(n):
	primes = {}
	while n % 2 == 0 and n > 0:
		primes[2] = primes.get(2, 0) + 1
		n = n // 2
	for i in range(3, int(n ** 0.5) + 2, 2):
		while n % i == 0 and n > 0:
			primes[i] = primes.get(i, 0) + 1
			n = n // i
	if n > 2:
		primes[n] = primes.get(n, 0) + 1
	return primes

def power(x, y, p):
	res = 1
	x = x % p
	if x == 0:
		return 0
	while y > 0:
		if y & 1 == 1:
			res = res * x % p
		y = y >> 1
		x = x * x % p
	return res

def swap(a, b):
	temp = a
	a = b
	b = temp
	return (a, b)

def find(x, link):
	p = x
	while p != link[p]:
		p = link[p]
	while x != p:
		nex = link[x]
		link[x] = p
		x = nex
	return p

def union(x, y, link, size):
	x = find(x, link)
	y = find(y, link)
	if size[x] < size[y]:
		(x, y) = swap(x, y)
	if x != y:
		size[x] += size[y]
		link[y] = x

def sieve(n):
	prime = [True for i in range(n + 1)]
	p = 2
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	return prime
MAXN = int(10000000.0 + 5)

def spf_sieve():
	spf[1] = 1
	for i in range(2, MAXN):
		spf[i] = i
	for i in range(4, MAXN, 2):
		spf[i] = 2
	for i in range(3, ceil(MAXN ** 0.5), 2):
		if spf[i] == i:
			for j in range(i * i, MAXN, i):
				if spf[j] == j:
					spf[j] = i

def factoriazation(x):
	ret = {}
	while x != 1:
		ret[spf[x]] = ret.get(spf[x], 0) + 1
		x = x // spf[x]
	return ret

def int_array():
	return list(map(int, input().strip().split()))

def str_array():
	return input().strip().split()
MOD = int(1000000000.0) + 7
CMOD = 998244353
INF = float('inf')
NINF = -float('inf')
n = int(input())
s = []
for _ in range(n):
	s.append(set(list(input().strip())))
link = [i for i in range(n)]
size = [1] * n
for i in range(97, 97 + 26):
	x = chr(i)
	inds = []
	for i in range(n):
		if x in s[i]:
			inds.append(i)
	for j in range(len(inds) - 1):
		union(inds[j], inds[j + 1], link, size)
ans = set()
for i in range(n):
	ans.add(find(link[i], link))
print(len(ans))


def dfs(n):
	global visited
	global chars
	visited[n] = True
	adj = list(set(chars.get(n, [])))
	for node in adj:
		if not visited[node]:
			dfs(node)
n = int(input())
chars = {}
visited = {}
while n:
	n -= 1
	s = list(set(input()))
	flag = 0
	if not s[0] in chars:
		chars[s[0]] = []
		visited[s[0]] = False
	for i in range(1, len(s)):
		chars[s[0]].append(s[i])
		if not s[i] in chars:
			chars[s[i]] = [s[0]]
		else:
			chars[s[i]].append(s[0])
		visited[s[i]] = False
ans = 0
for (k, v) in chars.items():
	if not visited[k]:
		dfs(k)
		ans += 1
print(ans)


import sys
input = sys.stdin.readline

class DisjointSet:

	def __init__(self, n):
		self.par = [i for i in range(n)]
		self.size = [1] * n
		self.rank = [0] * n

	def root(self, x):
		if self.par[x] == x:
			return x
		else:
			self.par[x] = self.root(self.par[x])
			return self.par[x]

	def has_same_root(self, x, y):
		return self.root(x) == self.root(y)

	def get_size(self, x):
		return self.size[self.root(x)]

	def unite(self, x, y):
		x = self.root(x)
		y = self.root(y)
		if self.rank[x] < self.rank[y]:
			self.par[x] = y
		else:
			self.par[y] = x
			if self.rank[x] == self.rank[y]:
				self.rank[x] += 1
n = int(input())
password = []
DS = DisjointSet(26)
seen = set()
orda = ord('a')
for i in range(n):
	password = input().rstrip()
	if len(password) == 1:
		seen.add(password)
		continue
	for (ch1, ch2) in zip(password, password[1:]):
		DS.unite(ord(ch1) - orda, ord(ch2) - orda)
		seen.add(ch1)
		seen.add(ch2)
pars = set()
for item in seen:
	pars.add(DS.root(ord(item) - orda))
print(len(pars))


import os
from io import BytesIO, IOBase
import sys
from collections import defaultdict, deque, Counter
from math import sqrt, pi, ceil, log, inf, gcd, floor
from itertools import combinations, permutations
from bisect import *
from fractions import Fraction

def find(p, c):
	b = []
	while p[c] != c:
		b.append(c)
		c = p[c]
	for i in b:
		p[i] = c
	return c

def main():
	p = [i for i in range(26)]
	s = [1] * 26
	d = [0] * 26
	for j in range(int(input())):
		b = [0] * 26
		for i in input().strip():
			b[ord(i) - 97] = 1
		(ind, ma) = (0, 0)
		for i in range(26):
			if b[i]:
				d[i] = 1
				y = find(p, i)
				if ma < s[y]:
					ma = s[y]
					ind = i
		for i in range(26):
			if p[i] != p[ind] and b[i]:
				s[p[ind]] += s[p[i]]
				p[p[i]] = p[ind]
	c = 0
	for i in range(26):
		if d[i]:
			if i == find(p, i):
				c += 1
	print(c)
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')
main()


class Disjoint_set:

	class node:

		def __init__(self, a):
			self.value = a
			self.p = self
			self.rank = 0

	def __init__(self, a):
		self.data = {i: self.make_set(i) for i in range(1, a + 1)}

	def make_set(self, val):
		return self.node(val)

	def union(self, x, y):
		self.link(self.find_set(self.data[x]), self.find_set(self.data[y]))

	def link(self, val1, val2):
		if val1.rank > val2.rank:
			val2.p = val1
			val1.rank += 1
		else:
			val1.p = val2
			val2.rank += 1

	def find_set(self, val):
		if val.p.value != val.value:
			val.p = self.find_set(val.p)
		return val.p

	def solution(self):
		pr = {}
		parent = {}
		for i in self.data:
			vl = self.find_set(self.data[i]).value
			parent[i] = vl
			if vl in pr:
				pr[vl] += 1
			else:
				pr[vl] = 1
		return pr

def main():
	n = int(input())
	s = [''.join(list(set(input()))) for i in range(n)]
	adj = [[] for _ in range(26)]
	s = list(set(s))
	l = len(s)
	for (k, i) in enumerate(s):
		for j in i:
			adj[ord(j) - ord('a')].append(k + 1)
	ds = Disjoint_set(l)
	done = set()
	for i in range(26):
		if adj[i]:
			x = adj[i][0]
			for j in adj[i]:
				if x != j and (x, j) not in done and ((j, x) not in done):
					ds.union(x, j)
					done.add((x, j))
	sl = ds.solution()
	print(len(sl))
main()


import sys, math, itertools
from collections import Counter, deque, defaultdict
from bisect import bisect_left, bisect_right
mod = 10 ** 9 + 7
INF = float('inf')

def inp():
	return int(sys.stdin.readline())

def inpl():
	return list(map(int, sys.stdin.readline().split()))

class UnionFind:

	def __init__(self, n):
		self.n = n
		self.parents = [-1] * n

	def find(self, x):
		if self.parents[x] < 0:
			return x
		else:
			self.parents[x] = self.find(self.parents[x])
			return self.parents[x]

	def union(self, x, y):
		x = self.find(x)
		y = self.find(y)
		if x == y:
			return
		if self.parents[x] > self.parents[y]:
			(x, y) = (y, x)
		self.parents[x] += self.parents[y]
		self.parents[y] = x

	def size(self, x):
		return -self.parents[self.find(x)]

	def same(self, x, y):
		return self.find(x) == self.find(y)

	def members(self, x):
		root = self.find(x)
		return [i for i in range(self.n) if self.find(i) == root]

	def roots(self):
		return [i for (i, x) in enumerate(self.parents) if x < 0]

	def group_count(self):
		return len(self.roots())

	def all_group_members(self):
		return {r: self.members(r) for r in self.roots()}
n = inp()
uf = UnionFind(26)
a = []
se = set()
for _ in range(n):
	s = sys.stdin.readline()
	s = list(set(s[:len(s) - 1]))
	for x in s:
		se.add(ord(x) - ord('a'))
	a.append((s, len(s)))
a.sort(key=lambda x: x[1], reverse=True)
for (x, ln) in a:
	if ln == 1:
		break
	for i in range(ln - 1):
		for j in range(i + 1, ln):
			uf.union(ord(x[i]) - ord('a'), ord(x[j]) - ord('a'))
res = set()
for i in range(26):
	if not i in se:
		continue
	res.add(uf.find(i))
print(len(res))


arr = [''] * 26
ans = 0
for i in range(int(input())):
	passwords = input()
	index = ord(passwords[0]) - 97
	arr[index] += passwords
	arr[index] = ''.join(set(arr[index]))
for i in range(len(arr)):
	for j in range(len(arr)):
		if i != j:
			for z in arr[i]:
				if z in arr[j]:
					arr[i] += arr[j]
					arr[j] = ''
for i in arr:
	if i != '':
		ans += 1
print(ans)


from sys import stdin, stdout
from collections import defaultdict
input = stdin.readline
n = int(input())
s = [chr(97 + i) for i in range(26)]
adj = defaultdict(list)
for i in range(n):
	st = input().strip()
	for j in set(st):
		adj[ord(j) - 97].append(26 + i)
		adj[26 + i].append(ord(j) - 97)
cmps = 0
v = [0] * (n + 27)
for i in range(26):
	if not v[i] and len(adj[i]) != 0:
		char = chr(97 + i)
		q = [i]
		while q:
			e = q.pop()
			v[e] = 1
			for j in adj[e]:
				if not v[j]:
					q.append(j)
		cmps += 1
print(cmps)


import sys
n = int(input())
alpha = 'abcdefghijklmnopqrstuvwxyz'
hashmap = {x: [] for x in alpha}
for i in range(n):
	a = sys.stdin.readline()
	for l in alpha:
		if l in a:
			hashmap[l].append(i)

class DisjSet:

	def __init__(self, n):
		self.rank = [1] * n
		self.parent = [i for i in range(n)]
		self.extra = []

	def find(self, x):
		if self.parent[x] != x:
			self.parent[x] = self.find(self.parent[x])
		return self.parent[x]

	def Union(self, x, y):
		xset = self.find(x)
		yset = self.find(y)
		if xset == yset:
			self.extra.append((x, y))
			return
		if self.rank[xset] < self.rank[yset]:
			self.parent[xset] = yset
		elif self.rank[xset] > self.rank[yset]:
			self.parent[yset] = xset
		else:
			self.parent[yset] = xset
			self.rank[xset] = self.rank[xset] + 1
obj = DisjSet(n)
for l in hashmap:
	for i in hashmap[l][1:]:
		obj.Union(hashmap[l][0], i)
unique = set()
for l in hashmap:
	for i in hashmap[l]:
		unique.add(obj.find(i))
print(len(unique))


import sys
global comp, par, rank
par = {}
comp = 1
rank = {}

def find(u):
	global comp, par, rank
	if par.get(u) == None or par[u] == u:
		if rank.get(u) == None:
			rank[u] = 1
		par[u] = u
		return u
	p = find(par[u])
	par[u] = p
	rank[p] += rank[u]
	return p

def union(u, v):
	global comp, par, rank
	p1 = find(u)
	p2 = find(v)
	if p1 == p2:
		return
	if rank[p1] > p2:
		par[p2] = p1
		rank[p1] += rank[p2]
	else:
		par[p1] = p2
		rank[p2] += rank[p1]
	comp -= 1
n = int(sys.stdin.readline())
comp = n
d = {}
for i in range(n):
	s = sys.stdin.readline().strip()
	for j in s:
		if j not in d:
			d[j] = set()
		d[j].add(i)
for i in d:
	p = d[i].pop()
	for k in d[i]:
		union(p, k)
print(comp)


import string

def count_connected_components(graph):
	seen = set()
	count = 0
	for k in graph:
		if k in seen:
			continue
		count += 1
		seen.add(k)
		stack = list()
		stack.append(k)
		while stack:
			cur = stack.pop()
			for neighbour in graph[cur]:
				if neighbour not in seen:
					seen.add(neighbour)
					stack.append(neighbour)
	return count
graph2 = {k: set() for k in string.ascii_lowercase}
for _ in range(int(input())):
	s = input()
	cs = set(s)
	for c in cs:
		graph2[c] |= cs
graph = {k: s for (k, s) in graph2.items() if len(s) > 0}
print(count_connected_components(graph))


from sys import stdin
from collections import deque
input = stdin.readline
n = int(input())
letters = [deque() for i in range(26)]
nums = [deque() for i in range(n)]
lettersVisited = [False for i in range(26)]
numsVisited = [False for i in range(n)]
for i in range(n):
	for j in [ord(x) - ord('a') for x in input().rstrip()]:
		nums[i].appendleft(j)
		letters[j].appendleft(i)

def dfs1(i):
	lettersVisited[i] = True
	for to in letters[i]:
		if not numsVisited[to]:
			dfs2(to)

def dfs2(i):
	numsVisited[i] = True
	for to in nums[i]:
		if not lettersVisited[to]:
			dfs1(to)
tot = 0
for i in range(26):
	if len(letters[i]) > 0 and (not lettersVisited[i]):
		dfs1(i)
		tot += 1
print(tot)