Problem:
 Score : 300 points    Problem Statement We will call a string obtained by arranging the characters contained in a string a in some order, an anagram of a .  For example, greenbin is an anagram of beginner . As seen here, when the same character occurs multiple times, that character must be used that number of times.  Given are N strings s_1, s_2, \ldots, s_N . Each of these strings has a length of 10 and consists of lowercase English characters. Additionally, all of these strings are distinct. Find the number of pairs of integers i, j  (1 \leq i < j \leq N) such that s_i is an anagram of s_j .      Constraints  2 \leq N \leq 10^5  s_i is a string of length 10 .  Each character in s_i is a lowercase English letter.  s_1, s_2, \ldots, s_N are all distinct.         Input Input is given from Standard Input in the following format:  N  s_1  s_2  :  s_N       Output Print the number of pairs of integers i, j  (1 \leq i < j \leq N) such that s_i is an anagram of s_j .        Sample Input 1 3
acornistnt
peanutbomb
constraint      Sample Output 1 1  s_1 =  acornistnt is an anagram of s_3 =  constraint . There are no other pairs i, j such that s_i is an anagram of s_j , so the answer is 1 .       Sample Input 2 2
oneplustwo
ninemodsix      Sample Output 2 0  If there is no pair i, j such that s_i is an anagram of s_j , print 0 .       Sample Input 3 5
abaaaaaaaa
oneplustwo
aaaaaaaaba
twoplusone
aaaabaaaaa      Sample Output 3 4  Note that the answer may not fit into a 32 -bit integer type, though we cannot put such a case here.

Solution (Python):
from collections import defaultdict
from math import factorial


def com(n,r):
    return factorial(n) // (factorial(n-r)*factorial(r))

N = int(input())
ans = 0
anss = defaultdict(int)
for i in range(N):
    t = input()
    ts = ''.join(c for c in sorted(t))
    anss[ts] += 1

for k in anss.keys():
    if anss[k] > 1:
        ans += com(anss[k],2)
print(ans)

Solution (Python):

import collections
from operator import mul
from functools import reduce

def cmb(n,r):
    r = min(n-r,r)
    if r == 0: return 1
    over = reduce(mul, range(n, n - r, -1))
    under = reduce(mul, range(1,r + 1))
    return over // under


N = int(input())
s = []
for i in range(N):
    tmp = list(input())
    tmp = sorted(tmp)
    tmp = ','.join(tmp)
    s.append(tmp)
c = collections.Counter(s)
ans_list = c.values()
ans_list = [i for i in ans_list if i >= 2]
ans = 0
if len(ans_list) != 0:
    for i in ans_list:
        ans += cmb(i, 2)
    print(ans)
else:
    print(ans)

Solution (Python):
n = int(input())

s = []
dic = {}
ans = 0
for i in range(n):
    si = input()
    si = sorted(si)
    ss = ""
    for a in si:
        ss += a
    if(ss in dic):
        dic[ss] += 1
    else:
        dic[ss] = 1

for e in dic:
    if(dic[e] == 1):
        continue
    ans += dic[e] * (dic[e] - 1) // 2

print(ans)

Solution (Python):

import sys
from collections import defaultdict
from collections import Counter

def solve(N: int, s: "List[str]"):
    count_dict = defaultdict(int)
    for i, string in enumerate(s):
        string = list(string)
        info = sorted(Counter(string).items(), key=lambda x:x[0])
        info = tuple([tuple([i, j]) for i, j in info])
        count_dict[info] += 1

    result = 0
    for _, v in count_dict.items():
        result += int((v-1) * 0.5 * (1 + v-1))
    print(result)
    return



def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    N = int(next(tokens))  
    s = [ next(tokens) for _ in range(N) ]  
    solve(N, s)

if __name__ == '__main__':
    main()

Solution (Python):
import math
N = int(input())
ss = []
ans = 0
def comb(l, r):
    if l == 0: return 0
    if l == 1: return 0
    return math.factorial(l)//(math.factorial(l-r)*math.factorial(r))

for _ in range(N):
    s = list(input())
    s.sort()
    ss.append(s)
ss.sort()

cnt = 1
for i in range(N-1):
    if ss[i] == ss[i + 1]:
        cnt += 1
    else:
        ans += comb(cnt,2)
        cnt = 1
ans += comb(cnt,2)        
print(ans)

Solution (Python):

n = int(input())

s = []
for i in range(n):
    word = input()
    s_li = []
    for j in word:
        s_li.append(j)
    s_li.sort()
    s.append(s_li)
s.sort()

ans = 0
memo = 0
for i in range(n-1):
    if s[i] == s[i+1]:
        ans += 1+memo
        memo +=1
    else:
        memo = 0
    
print(ans)

Solution (Python):
import sys
input=sys.stdin.readline
from collections import Counter
n=int(input())
S=[]
for _ in range(n):
    s=list(input().strip())
    s.sort()
    s=''.join(s)
    S.append(s)
c=Counter(S)
ans=0
for v in c.values():
    ans+=(v*(v-1))//2
print(ans)

Solution (Python):
import collections


if __name__ == '__main__':
    n = int(input())
    s = ["".join(sorted(list(input()))) for i in range(n)]

    c = collections.Counter(s)

    answer = 0
    for cnt in c.values():
        answer += cnt*(cnt-1)//2
    print(answer)

Solution (Python):
n=int(input())
dic={}
ans=0
for i in range(n):
	s=input()
	alpha=[0 for j in range(26)]
	for c in s:
		alpha[ord(c)-97]+=1
	val=int(''.join(str(x) for x in alpha))
	if(val in dic.keys()):
		dic[val]+=1
	else:
		dic[val]=1
for k in list(dic.values()):
	val=k-1
	ans+=((val*(val+1))//2)
print(ans)

Solution (Python):
N = int(input())

strings = []
for i in range(N):
    strings.append(''.join(sorted(input().strip())))
strings.sort()

ans = 0
last_string = ''
c = 1
for s in strings:
    if last_string == s:
        c += 1
    else:
        last_string = s
        if c != 1:
            ans += c * (c - 1) // 2
        c = 1
if c != 1:
    ans += c * (c - 1) // 2

print(ans)

Solution (Python):
import collections
from math import factorial

N =int(input())
cnts = [frozenset(collections.Counter(list(input())).items()) for _ in range(N)]
xs = collections.Counter(cnts)
ans = 0
for _,v in xs.items():
    if v>1:
        a = factorial(v) // (2*factorial(v - 2))
        ans +=a
print(ans)

Solution (Python):
import collections

N=int(input())
S=[]

for i in range(N):
    s=list(str(input()))
    s.sort()
    S.append(''.join(s))
c=list(collections.Counter(S).values())
c=[int(i*(i-1)/2) for i in c if i>=2]
print(sum(c))

Solution (Python):
n = int(input())
dic = {}
for _ in range(n):
    s = list(input())
    k = ''.join(sorted(s))
    if k in dic:
       dic[k] += 1
    else:
       dic[k] = 1
ans = sum([v * (v - 1) // 2 for v in dic.values()])
print(ans)

Solution (Python):
N=int(input())
S_list=[]
for n in range(N):
    S_list.append(input())
    
S_list=sorted([sorted(s) for s in S_list])   
count_list=[]
tmp_count=0
for n in range(N-1):
    left=S_list[n]
    right=S_list[n+1]
    if left==right:
        tmp_count+=1
    else:
        count_list.append(tmp_count+1)
        tmp_count=0
count_list.append(tmp_count+1)
count_list=[c*(c-1)//2 for c in count_list]
print(sum(count_list))

Solution (Python):
N = int(input())
S = []
A = {}
for i in range(N):
    sortedstring = ''.join(sorted(input()))
    if sortedstring in A:
        A[sortedstring]+=1
    else:
        A[sortedstring]= 1

Nacro = 0
for k, v in A.items():
    Nacro+=int(v*(v-1)/2.)
    

print(Nacro)

Solution (Python):
n = int(input())
S = []
for _ in range(n):
    S.append(sorted(input()))

count = 1
ans = 0
pre = ""
for s in sorted(S):
    if s == pre:
        count += 1
    else:
        ans += (count*(count-1))//2
        count = 1
    pre = s
ans += (count*(count-1))//2
print (ans)

Solution (Python):
import collections
N = int(input())
s = []
for i in range(N):
    s.append(''.join(sorted(input())))
c = collections.Counter(s)
cnt = 0
for k, v in c.items():
    cnt += v*(v-1)//2
print(cnt)

Solution (Python):







n = int(input())
ss = []
for i in range(n):
    ss.append(input())

newss = []
for s in ss:
    newss.append("".join(sorted(list(s))))
newss = sorted(newss)
newss.append("XXXXXXXXXX")

last = newss[0]
count = 1
res = 0
for i in range(1,n+1):
    if last == newss[i]:
        count +=1

    else:
        res += (count*(count-1))//2

        count = 1
    last = newss[i]
print(res)

Solution (Python):
from operator import mul
from functools import reduce
 
def cmb(n,r):
    r = min(n-r,r)
    if r==0: return 1
    over = reduce(mul, range(n, n - r, -1))
    under = reduce(mul, range(1,r + 1))
    return over // under
  

from collections import defaultdict
dic = defaultdict(int)
N = int(input())
for n in range(N):
  s = list(input())
  s.sort()
  k = tuple(s)
  dic[k] += 1
ans = 0
for k in dic.keys():
  if dic[k]<2:
    continue
  ans += cmb(dic[k],2)
print(ans)

Solution (Python):
from collections import Counter

N = int(input())

s_sorted_list = []
for i in range(N):
    s_sorted = ''.join(sorted(input()))
    s_sorted_list.append(s_sorted)

count = Counter(s_sorted_list)
count = count.most_common()

ans = 0
for i in range(len(count)):
    if count[i][1] == 1:
        break
    else:
        ans += count[i][1]*(count[i][1]-1)//2
print(ans)

Solution (Python):
from operator import mul
from functools import reduce
from collections import Counter

def ncr(n,r):
    r = min(n-r,r)
    if r == 0: return 1
    over = reduce(mul, range(n, n - r, -1))
    under = reduce(mul, range(1,r + 1))
    return over // under

n=int(input())
s=["".join(sorted(input())) for _ in range(n)]

c = Counter(s)
ans=0
for v in c.values():
    if v <= 1: continue
    ans += ncr(v, 2)

print(ans)

Solution (Python):

import sys

sys.setrecursionlimit(1000000)

nCr = {}


def cmb(n, r):
    if r == 0 or r == n:
        return 1
    if r == 1:
        return n
    if (n, r) in nCr:
        return nCr[(n, r)]
    nCr[(n, r)] = cmb(n-1, r) + cmb(n-1, r-1)
    nCr[(n, r)] = nCr[(n, r)]
    return nCr[(n, r)]


def acinput():
    return list(map(int, input().split(" ")))


N = int(input())
dct = {}

for i in range(N):
    tmp = input()
    
    tmp = "".join(sorted(tmp))
    try:
        dct[tmp] += 1
    except:

        dct[tmp] = 1


res = 0
for sdct in dct:
    if dct[sdct] > 1:
        res += cmb(dct[sdct], 2)

print(res)

Solution (Python):
from collections import Counter
c = Counter(frozenset(Counter(input()).items()) for _ in range(int(input())))
print(sum(x * (x - 1) // 2 for x in c.values()))

Solution (Python):
import math
N = int(input())
S = []
for i in range(N):
    s = input()
    s = sorted(s)
    S.append(''.join(s))
S = sorted(S)
cnt = 0
ans = 0
for i in range(N-1):
    if S[i] == S[i+1]:
        cnt += 1
    else:
        if cnt > 0:
            ans += math.factorial(cnt+1) // (math.factorial(cnt + 1 - 2) * math.factorial(2))
        cnt = 0
if cnt > 0:
    ans += math.factorial(cnt+1) // (math.factorial(cnt + 1 - 2) * math.factorial(2))
print(ans)

Solution (Python):
from collections import Counter
N=int(input())

st = []
for n in range(N):
    s = ''.join(sorted(input()))
    st.append(s)

a = 0
for k, f in dict(Counter(st)).items():
    if f == 1:
        continue
    else:
       a += f*(f-1)/2

print(int(a))

Solution (Python):
import sys
from collections import Counter
import math
input = sys.stdin.readline

N = int(input())
s = [input().strip() for _ in range(N)]
ans = []
for word in s:
    temp = []
    for letter in word:
        temp.append(letter)
        temp.sort()
    temp2 = ''
    for value in temp:
        temp2 += value
    ans.append(temp2)
fin = Counter(ans)
out = 0
for value in fin.values():
    if value > 1:
        out += int(math.factorial(value) / math.factorial(value-2) / 2)
print(out)

Solution (Python):

import sys

def encode(s):
    code = [0] * 26
    for i in range(10):
        code[ord(s[i]) - ord('a')] += 1
    return code

def codeEqual(code1, code2):
    for i in range(26):
        if code1[i] != code2[i]:
            return False
    return True

def solve(N: int, s: "List[str]"):
    list_code = []
    for i in range(N):
        code = encode(s[i])
        list_code += [code]
    list_code = sorted(list_code)
    
    list_same = []
    count = 1
    for i in range(N-1):
        if codeEqual(list_code[i], list_code[i+1]):
            count += 1
        else:
            list_same += [count]
            count = 1
    if count > 1:
        list_same += [count]
    
    final_count = 0
    for same in list_same:
        if same > 1:
            final_count += same * (same - 1) // 2
    print(final_count)
            
    return


def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    N = int(next(tokens))  
    s = [ next(tokens) for _ in range(N) ]  
    solve(N, s)

if __name__ == '__main__':
    main()

Solution (Python):


from collections import defaultdict

N = int(input())

s = []
for i in range(N):
    s.append(input())

c = defaultdict(int)
for ss in s:
    c["".join(sorted(list(ss)))] += 1

result = 0
for v in c.values():
    result += v * (v - 1) // 2

print(result)

Solution (Python):
def main():
    n = int(input())
    s = []
    for i in range(n):
        stmp = input()
        stmp = sorted(stmp)
        stmp = ''.join(stmp)
        s.append(stmp)
    s.sort()

    anslist = []
    flag = False
    counter = 1
    place = 0
    for i in range(n):
        while s[place] == s[place+counter]:
            counter +=1
            if place+counter == n:
                break
        anslist.append(counter)
        place += counter
        counter = 1
        if place >= n-1:
            break
    ans = 0
    for i in anslist:
        ans += (i*(i-1))//2
        
    print(ans)

main()

Solution (Python):
import math
def combinations_count(n, r):
    return math.factorial(n) // (math.factorial(n - r) * math.factorial(r))

n=int(input())
s=["".join(sorted(list(input()))) for i in range(n)]
from collections import Counter
hoge=Counter(s)
res = 0
for key,value in hoge.items():
  if(value >= 2):
    res += combinations_count(value, 2)
print(res)

Solution (Python):
import collections
import math

def combinations_count(n, r):
    return math.factorial(n) // (math.factorial(n - r) * math.factorial(r))

def main():
    N = int(input())

    s = [""] * N
    for i in range(N):
        s[i] = ''.join(sorted(input()))
    S = collections.Counter(s)
    
    cnt = 0
    for i in S.values():
        if i > 1:
            cnt += combinations_count(i, 2)

    print(cnt)
main()

Solution (Python):
import sys
N = int(raw_input())

d = dict()
for i in range(N):
    s = raw_input() 
    if d.get(tuple(sorted(list(s)))) is None:
        d[tuple(sorted(list(s)))] = 1
    else:
        d[tuple(sorted(list(s)))] += 1


ret = 0
for x in d.values():
    if x == 1:
        continue
    ret += (x * (x - 1)) / 2


print ret

Solution (Python):
import collections
n = int(input())



stock = collections.Counter()
ans = 0


for i in range(n):
  str = tuple(sorted(collections.Counter(input().strip()).items()))
  ans += stock[str]
  stock[str] += 1

print(ans)

Solution (Python):

x = []
n = int(input())
for i in range(n):
    x.append(sorted(list(input())))
x = sorted(x)
s = 0
su = 0

for i in range(0,n-1):
    if(x[i] == x[i+1]):
        s=s+1
        continue
    else:
        su += s*(s+1)//2
        s=0
su += s*(s+1)//2
print(su)

Solution (Python):
n = input()
d = {}
x = 0
for _ in xrange(n):
	ls = ''.join(sorted(raw_input()))
	if ls in d:
		x += d[ls]
		d[ls] += 1
	else:
		d[ls] = 1
print x

Solution (Python):
N = int(input())

S = [input() for _ in range(N)]
PS = sorted(["".join([c + str(s.count(c)) for c in sorted(list(set(s)))]) for s in S])

t = 0
i = 0
while i < len(PS):
    j = i + 1
    while j < len(PS) and PS[j] == PS[i]:
        j += 1
    n = j - i

    t += n * (n - 1) // 2

    i = j

print(t)

Solution (Python):
import collections

N = int(input())

a_list = []
for i in range(N):
    a = input()
    a = ''.join(sorted(a))
    a_list.append(a)

a_list = sorted(a_list)

c = collections.Counter(a_list)

sum = 0
for k, v in c.most_common():
    sum += v*(v-1)//2
print(sum)

Solution (Python):

import sys
from collections import Counter
from scipy.misc import comb

def solve(N: int, s: "List[str]"):
    print(sum([comb(v, 2, exact=True) for v in Counter(s).values()]))

def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    N = int(next(tokens))  
    s = [ ' '.join(sorted(next(tokens))) for _ in range(N) ]  
    solve(N, sorted(s))

if __name__ == '__main__':
    main()

Solution (Python):
n = int(input())
c = []
for _ in range(n):
    s = input()
    tmp = [0] * 26
    for cc in s:
        tmp[ord(cc) - ord('a')] += 1
    c.append(tmp)
c.sort()
tmp = 1
ans = 0
for i in range(1, n):
    if c[i-1] == c[i]:
        tmp += 1
    else:
        if tmp > 1:
            ans += (tmp * (tmp-1) )// 2
        tmp = 1
if tmp > 1:
    ans += tmp * (tmp-1) // 2
print(ans)

Solution (Python):
import collections
import math

def combinations_count(n, r):
    return math.factorial(n) // (math.factorial(n - r) * math.factorial(r))

N = int(input())
s = [input() for _ in range(N)]
idx = 0
for i in s:
    t = sorted(i)
    s[idx] = "".join(t)
    idx += 1
s = collections.Counter(s)
ans = 0
for i in s:
    if s[i] > 1:
        ans += combinations_count(s[i], 2)
print(ans)

Solution (Python):
from collections import Counter
from itertools import accumulate
N = int(input())
S = [''.join(sorted(list(input()))) for i in range(N)]
S_counter = Counter(S)

acc = list(accumulate(list(range(10 ** 5 + 1))))

check = set()
ans = 0
for s_c in S_counter:
    if s_c not in check:
        x = S_counter[s_c]
        ans += acc[x - 1]
    check.add(s_c)

print(ans)

Solution (Python):
n = int(input())
ls = [''.join(sorted(input())) for j in range(n)]
ls = sorted(ls)

s = ls[0]
i = 1
count = 1
ans = 0
while i < n:
  if ls[i] == s:
    count += 1
    i += 1
    if i == n:
      ans += count*(count-1)//2
  else:
    ans += count*(count-1)//2
    count = 1
    s = ls[i]
    i += 1
  

print(ans)

Solution (Python):
n = int(input())
from collections import defaultdict


s_input_list = []

for i in range(n):
    tmp = input()
    s_input_list.append(tmp)


d = defaultdict(list)

s_sorted_list = []
for s in s_input_list:
    tmp = list(s)
    tmp.sort()
    
    s_sorted_list.append("".join(tmp))



ans = 0
for i in range(n):
    d[s_sorted_list[i]].append(i)

for key,value in d.items():
    v = len(value)
    ans = ans + int(v*(v-1)/2)


print(ans)

Solution (Python):
import collections

N = int(input())
S = [list(input()) for _ in range(N)]
S2 = ['0' for _ in range(N)]
for i in range(N):
    S[i].sort()
    S2[i] = ' '.join(S[i])

c = collections.Counter(S2)
ans = 0
for i in c.values():
    if i==1:continue
    ans += (i-1)*(i)//2
    
    
print(ans)

Solution (Python):
import collections
import sys
sys.setrecursionlimit(100000000)

from math import factorial
def ncr(n,r):
 if n==r:
  return 1
 elif n>2:
  return factorial(n) // factorial(r) // factorial(n - r)
 else:
  return 1
def solve():
  n = int(input())
  query = []
  for i in range(n):
    a = sorted(input())
    query.append("".join(a))

  c = collections.Counter(query)
  ans = 0
  tmp = list(c.values())

  for i in tmp:
    if i >=2:
      ans += ncr(i,2)
  print(int(ans))
solve()

Solution (Python):
N = int(input())
a = []
for i in range(N):
    lis = sorted([s for s in input()])
    ans = ''
    for i in lis:
        ans = ans + i    
    a.append(ans)
    
import collections
count = dict(collections.Counter(a))
ans = 0
for k,v in count.items():
    ans += v * (v-1) / 2
print(int(ans))

Solution (Python):

import sys
import math
from bisect import bisect_left
from bisect import bisect_right
import collections
import copy
import heapq
from collections import defaultdict
from heapq import heappop, heappush
import itertools
input = sys.stdin.readline






















def inputInt(): return int(input())
def inputMap(): return map(int, input().split())
def inputList(): return list(map(int, input().split()))

def main():
	N = inputInt()
	s = []
	for i in range(N):
		tmp = input()
		tmp = list(tmp)
		s.append(tmp[:-1])

	ans = {}
	for i in s:
		i.sort()
		
		tmp = "".join(i)

		if tmp in ans:
			ans[tmp] += 1
		else:
			ans[tmp] = 1

	oo = 0
	
	for v in ans.values():
		if v == 1:
			continue
		elif v == 2:
			oo += 1
		else:
			tmp = 0
			for i in range(1,v):
				tmp += i
			oo += tmp

	print(oo)

if __name__ == "__main__":
	main()

Solution (Python):
n = int(input())
s = dict()
for i in range(0,n):
    a = list(input())
    a.sort()
    if str(a) in s:
        s[str(a)] += 1
    else:
        s[str(a)] = 1
ans = 0
for i in s:
    k = s[i]
    ans += int(k*(k-1)/2)

print(ans)

Solution (Python):
n = int(input())
s = []
for i in range(n):
    _s = list(input())
    _s.sort()
    s.append("".join(_s))
s.sort()
c = 0
ans = 0
for i in range(n - 1):
    if s[i] == s[i + 1]:
        c += 1
    else:
        ans += (c * (c + 1) // 2)
        c = 0
ans += (c * (c + 1) // 2)
print(ans)

Solution (Python):
import collections
from operator import mul
from functools import reduce

def cmb(n,r):
    r = min(n-r,r)
    if r == 0: return 1
    over = reduce(mul, range(n, n - r, -1))
    under = reduce(mul, range(1,r + 1))
    return over // under

n = int(input())
s = [''.join(sorted(input())) for _ in range(n)]
c = collections.Counter(s)
count = 0
for i in c.values():
  if i >= 2:
    count += cmb(i, 2)
print(count)

Solution (Python):

n = int(input())
dict = {}
for _ in range(n):
    ss = ""
    s = sorted(list(input()))
    ss = ss.join(s)
    if dict.get(ss) != None:
        dict[ss] = dict[ss] + 1
    else:
        dict[ss] = 1


ans = 0
for key, val in dict.items():
    ans += val * (val-1) // 2
print(ans)

Solution (Python):
def main(sample_file = ''):

    """ convenient functions
    
    
    
    
    
    """

    """convenient decorator
    
    
        
        
        
        
        
        
        
        
        
        
    """
    
    
    import sys
    sys.setrecursionlimit(10**7)
    from itertools import accumulate, combinations, permutations, product 
    
    from math import factorial, ceil, floor, sqrt
    def factorize(n):
        """return the factors of the Arg and count of each factor
        
        Args:
            n (long): number to be resolved into factors
        
        Returns:
            list of tuples: factorize(220) returns [(2, 2), (5, 1), (11, 1)]
        """
        fct = []  
        b, e = 2, 0  
        while b * b <= n:
            while n % b == 0:
                n = n // b
                e = e + 1
            if e > 0:
                fct.append((b, e))
            b, e = b + 1, 0
        if n > 1:
            fct.append((n, 1))
        return fct
    def combinations_count(n, r):
        """Return the number of selecting r pieces of items from n kinds of items.
        
        Args:
            n (long): number
            r (long): number
        
        Raises:
            Exception: not defined when n or r is negative
        
        Returns:
            long: number
        """
        
        if n < 0 or r < 0:
            raise Exception('combinations_count(n, r) not defined when n or r is negative')
        if n - r < r: r = n - r
        if r < 0: return 0
        if r == 0: return 1
        if r == 1: return n
        numerator = [n - r + k + 1 for k in range(r)]
        denominator = [k + 1 for k in range(r)]
        for p in range(2,r+1):
            pivot = denominator[p - 1]
            if pivot > 1:
                offset = (n - r) % p
                for k in range(p-1,r,p):
                    numerator[k - offset] /= pivot
                    denominator[k] /= pivot
        result = 1
        for k in range(r):
            if numerator[k] > 1:
                result *= int(numerator[k])
        return result
    def combinations_with_replacement_count(n, r):
        """Return the number of selecting r pieces of items from n kinds of items allowing individual elements to be repeated more than once.
        
        Args:
            n (long): number
            r (long): number
        
        Raises:
            Exception: not defined when n or r is negative
        
        Returns:
            long: number
        """
        if n < 0 or r < 0:
            raise Exception('combinations_with_replacement_count(n, r) not defined when n or r is negative')
        elif n == 0:
            return 1
        else:
            return combinations_count(n + r - 1, r)
    from bisect import bisect_left, bisect_right
    from collections import deque, Counter, defaultdict 
    from heapq import heapify, heappop, heappush, heappushpop, heapreplace,nlargest,nsmallest 
    from copy import deepcopy, copy 
    import operator
    from operator import itemgetter 
    
    
    from functools import reduce, lru_cache
    def chmin(x, y):
        """change minimum
        if x > y, x = y and return (x, True).
        convenient when solving problems of dp[i]
        
        Args:
            x (long): current minimum value
            y (long): potential minimum value
        
        Returns:
            (x, bool): (x, True) when updated, else (x, False)
        """
        if x > y:
            x = y
            return (x, True)
        else:
            return (x, False)
    def chmax(x, y):
        """change maximum
        if x < y, x = y and return (x, True).
        convenient when solving problems of dp[i]
        
        Args:
            x (long): current maximum value
            y (long): potential maximum value
        
        Returns:
            (x, bool): (x, True) when updated, else (x, False)
        """
        if x < y:
            x = y
            return (x, True)
        else:
            return (x, False)

    from fractions import gcd 
    def gcds(numbers):
        return reduce(gcd, numbers)
    def lcm(x, y):
        return (x * y) // gcd(x, y)
    def lcms(numbers):
        return reduce(lcm, numbers, 1)
    def make_divisors(n, reversed=False):
        """create list of divisors
        
        Args:
            number (int): number from which list of divisors is created
            reversed (bool, optional): ascending order if False. Defaults to False.
        
        Returns:
            list: list of divisors
        """
        divisors = set()
        for i in range(1, int(n**0.5)+1):
            if n % i == 0:
                divisors.add(i)
                divisors.add(n//i)
        return sorted(list(divisors),reverse=reversed)
    
    
    
    INF = 10 ** 18
    MOD = 10 ** 9 + 7
    modpow = lambda a, n, p = MOD: pow(a, n, p) 
    def modinv(a, p = MOD):
        
        
        return modpow(a, p-2, p)
    def modinv_list(n, p = MOD):
        if n <= 1:
            return [0,1][:n+1]
        else:
            inv_t = [0,1]
            for i in range(2, n+1):
                inv_t += [inv_t[p % i] * (p - int(p / i)) % p]
            return inv_t
    def modfactorial_list(n, p = MOD):
        if n == 0:
            return [1]
        else:
            l = [0] * (n+1)
            tmp = 1
            for i in range(1, n+1):
                tmp = tmp * i % p
                l[i] = tmp
            return l
    def modcomb(n, k, fac_list = [], p = MOD):
        
        
        from math import factorial
        if n < 0 or k < 0 or n < k: return 0
        if n == 0 or k == 0: return 1
        if len(fac_list) <= n:
            a = factorial(n) % p
            b = factorial(k) % p
            c = factorial(n-k) % p
        else:
            a = fac_list[n]
            b = fac_list[k]
            c = fac_list[n-k]
        return (a * modpow(b, p-2, p) * modpow(c, p-2, p)) % p
    def modadd(a, b, p = MOD):
        return (a + b) % MOD
    def modsub(a, b, p = MOD):
        return (a - b) % p
    def modmul(a, b, p = MOD):
        return ((a % p) * (b % p)) % p
    def moddiv(a, b, p = MOD):
        return modmul(a, modpow(b, p-2, p))

    class UnionFindTree:
        """union find tree class
        TODO: fix this description...
        how to use (example):
        >>  uf = UnionFindTree(N) 
        >>  if uf.find_root(a) == uf.find_root(b):
        >>      do something
        >>  else:
        >>      do something
        >>      uf.unite(a, b)
        """
        def __init__(self, N):
            self.root = [-1] * (N+1)
            self.rank   = [0]  * (N+1)
            self.connected_num = [1] * (N+1)
    
        def find_root(self,x):
            root = self.root
            while root[x] != -1:
                x = root[x]
            return x
    
        def unite(self,x,y):
            root = self.root
            rank = self.rank
            connected_num = self.connected_num
            find_root = self.find_root
    
            rx = find_root(x)
            ry = find_root(y)
            if rx != ry:
                if rank[rx] < rank[ry]:
                    root[rx] = ry
                    rx,ry = ry,rx
                else:
                    if rank[rx] == rank[ry]:
                        rank[rx] += 1
                    root[ry] = rx       
                connected_num[rx] += connected_num[ry]

    """ initialize variables and set inputs
    
        
        
    
        
        
        
        
        
        
        
    
        
        
        
        
    
        
        
    """

    
    r = lambda: sys.stdin.readline().strip()
    r_int = lambda: int(r())
    R = lambda: list(map(int, r().split()))
    Rmap = lambda: map(int, r().split())
    Rfloat = lambda: list(map(float, r().split()))
    Rtuple = lambda: tuple(map(int, r().split()))

    """ how to treat input
    
    
    
    
    
    
    
    """

    
    if sample_file:
        sys.stdin = open(sample_file)
        
    
    

    N = int(r())
    S = [0] * N
    for i in range(N):
        s = r()
        s = sorted(s)
        S[i] = s
    S.sort()
    cnt = 0
    tmp = ''
    ans = 0
    for i in S:
        if i == tmp:
            cnt += 1
        else:
            if cnt >= 2:
                ans += combinations_count(cnt, 2)
            cnt = 1
        tmp = i
    else:
        if cnt >= 2:
            ans += combinations_count(cnt, 2)
    print(ans)
    
    
    
    

    """memo: how to use defaultdict of list
    
    Dic = defaultdict(list)
    
    Dic[x].append(y)
    
    for k, v in Dic.items():
    """
    
    """memo: how to solve binary problems
    
    >>> a = 5
    >>> bin_str_a = format(a, '
    >>> print(bin_str_a)
    0b0101 
    """
    
    """memo: how to solve the problem
    creating simple test/answer
    greed
    simple dp
    graph
    """

if __name__ == '__main__':
    main()

Solution (Python):

import sys
import math
from bisect import bisect_left
from bisect import bisect_right
import collections
import copy
import heapq
from collections import defaultdict
from heapq import heappop, heappush
import itertools
input = sys.stdin.readline






















def inputInt(): return int(input())
def inputMap(): return map(int, input().split())
def inputList(): return list(map(int, input().split()))

def main():
	N = inputInt()
	s = []
	for i in range(N):
		tmp = input()
		tmp = list(tmp)
		s.append(tmp[:-1])

	ans = {}
	for i in s:
		i.sort()
		
		tmp = "".join(i)

		if tmp in ans:
			ans[tmp] += 1
		else:
			ans[tmp] = 1

	oo = 0
	
	for v in ans.values():
		if v == 1:
			continue
		elif v == 2:
			oo += 1
		else:
			tmp = 0
			for i in range(1,v):
				tmp += i
			oo += tmp

	print(oo)

if __name__ == "__main__":
	main()

Solution (Python):
n = int(input())
my_dict = {}
for i in range(n):
    sorted_str = ''.join(sorted(list(input())))
    if not sorted_str in my_dict:
        my_dict[sorted_str] = 1
    else:
        my_dict[sorted_str] += 1
        
        
        
ret = 0
my_list = [i for i in my_dict.values() if i>1]
for j in my_list:
    ret +=  (j * (j-1) // 2)
    
    
print(ret)