Given a string s consisting of only lowercase alphabet characters, count all pairs of substrings of s that are anagrams of each other.
Examples:
Input: s = "xyyx"
Output: 4
Explanation: Total anagrammatic substring pairs are: ["x", "x"], ["y", "y"], ["xy", "yx"], ["xyy", "yyx"]Input: s = "geeg"
Output: 4
Explanation: Valid anagrammatic substring pairs are: ["g", "g"], ["e", "e"], ["gee", "eeg"], ["ge", "eg"]Input: s = "aba"
Output: 2
Explanation: Anagrammatic substring pairs include: ["a", "a"], ["ab", "ba"]
Table of Content
[Naive Approach] Generate all substrings and Compare - O(n^5 log n) Time and O(n^3) Space
Generate all possible substrings of the given string and store them. Then compare every pair of substrings and check whether they are anagrams by sorting both strings. If two sorted substrings are equal, increment the count of anagrammatic pairs.
#include <iostream>
using namespace std;
bool isAnagram(string a, string b) {
if (a.length() != b.length())
return false;
sort(a.begin(), a.end());
sort(b.begin(), b.end());
return a == b;
}
int countAnagramPairs(string s) {
int n = s.length();
vector<string> substrings;
// Step 1: Generate all substrings
for (int i = 0; i < n; i++) {
for (int len = 1; len <= n - i; len++) {
substrings.push_back(s.substr(i, len));
}
}
int count = 0;
// Step 2: Compare every pair of substrings
for (int i = 0; i < substrings.size(); i++) {
for (int j = i + 1; j < substrings.size(); j++) {
if (isAnagram(substrings[i], substrings[j])) {
count++;
}
}
}
return count;
}
int main() {
string s = "abba";
cout << countAnagramPairs(s);
return 0;
}
import java.util.*;
public class GFG {
// Function to check if two strings are anagrams
static boolean isAnagram(String a, String b) {
if (a.length() != b.length())
return false;
char[] arr1 = a.toCharArray();
char[] arr2 = b.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
return Arrays.equals(arr1, arr2);
}
static int countAnagramPairs(String s) {
int n = s.length();
List<String> substrings = new ArrayList<>();
// Step 1: Generate all substrings
for (int i = 0; i < n; i++) {
for (int len = 1; len <= n - i; len++) {
substrings.add(s.substring(i, i + len));
}
}
int count = 0;
// Step 2: Compare every pair
for (int i = 0; i < substrings.size(); i++) {
for (int j = i + 1; j < substrings.size(); j++) {
if (isAnagram(substrings.get(i), substrings.get(j))) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
String s = "abba";
System.out.println(countAnagramPairs(s));
}
}
def isAnagram(a, b):
if len(a) != len(b):
return False
return sorted(a) == sorted(b)
def countAnagramPairs(s):
n = len(s)
substrings = []
# Step 1: Generate all substrings
for i in range(n):
for length in range(1, n - i + 1):
substrings.append(s[i:i+length])
count = 0
# Step 2: Compare every pair
for i in range(len(substrings)):
for j in range(i + 1, len(substrings)):
if isAnagram(substrings[i], substrings[j]):
count += 1
return count
s = "abba"
print(countAnagramPairs(s))
using System;
using System.Collections.Generic;
using System.Linq;
public class GFG
{
static bool IsAnagram(string a, string b)
{
if (a.Length != b.Length)
return false;
var arr1 = a.ToCharArray();
var arr2 = b.ToCharArray();
Array.Sort(arr1);
Array.Sort(arr2);
return new string(arr1) == new string(arr2);
}
static int CountAnagramPairs(string s)
{
int n = s.Length;
List<string> substrings = new List<string>();
// Step 1: Generate all substrings
for (int i = 0; i < n; i++)
{
for (int len = 1; len <= n - i; len++)
{
substrings.Add(s.Substring(i, len));
}
}
int count = 0;
// Step 2: Compare every pair
for (int i = 0; i < substrings.Count; i++)
{
for (int j = i + 1; j < substrings.Count; j++)
{
if (IsAnagram(substrings[i], substrings[j]))
{
count++;
}
}
}
return count;
}
public static void Main()
{
string s = "abba";
Console.WriteLine(CountAnagramPairs(s));
}
}
function isAnagram(a, b) {
if (a.length !== b.length)
return false;
return a.split('').sort().join('') ===
b.split('').sort().join('');
}
function countAnagramPairs(s) {
let n = s.length;
let substrings = [];
// Step 1: Generate all substrings
for (let i = 0; i < n; i++) {
for (let len = 1; len <= n - i; len++) {
substrings.push(s.substring(i, i + len));
}
}
let count = 0;
// Step 2: Compare every pair
for (let i = 0; i < substrings.length; i++) {
for (let j = i + 1; j < substrings.length; j++) {
if (isAnagram(substrings[i], substrings[j])) {
count++;
}
}
}
return count;
}
//Driver Code
let s = "abba";
console.log(countAnagramPairs(s));
Output
4
[Expected Approach 1] Sorted Substring Hashing - O(n^3 log n) Time and O(n^3) Space
The idea is to count pairs of substrings that are anagrams by exploiting the property that anagrams share the same sorted form. We generate all possible substrings, sort each substring, and store their frequencies in a map. If a sorted substring appears f times, then the number of valid anagrammatic pairs is f × (f - 1) / 2. This ensures we capture all unique anagram pairs efficiently using frequency counting.
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
int countAnagramPairs(string &s) {
int n = s.length();
int count = 0;
// Map to store frequency of sorted substrings
unordered_map<string, int> freqMap;
// Generate all substrings
for (int i = 0; i < n; i++) {
string curr = "";
for (int j = i; j < n; j++) {
curr += s[j];
// Sort characters of the current substring
string sortedStr = curr;
sort(sortedStr.begin(), sortedStr.end());
// Increment count of this sorted pattern
freqMap[sortedStr]++;
}
}
// Count total anagrammatic pairs from frequencies
for (auto &entry : freqMap) {
int f = entry.second;
// Choose any 2 from f => f * (f - 1) / 2
if (f > 1) {
count += (f * (f - 1)) / 2;
}
}
return count;
}
int main() {
string s = "xyyx";
cout << countAnagramPairs(s);
return 0;
}
import java.util.*;
class GfG {
public static int countAnagramPairs(String s) {
int n = s.length();
int count = 0;
// Map to store frequency of sorted substrings
HashMap<String, Integer> freqMap = new HashMap<>();
// Generate all substrings
for (int i = 0; i < n; i++) {
String curr = "";
for (int j = i; j < n; j++) {
curr += s.charAt(j);
// Sort characters of the current substring
char[] chars = curr.toCharArray();
Arrays.sort(chars);
String sortedStr = new String(chars);
// Increment count of this sorted pattern
freqMap.put(sortedStr, freqMap.getOrDefault(sortedStr, 0) + 1);
}
}
// Count total anagrammatic pairs from frequencies
for (Map.Entry<String, Integer> entry : freqMap.entrySet()) {
int f = entry.getValue();
// Choose any 2 from f => f * (f - 1) / 2
if (f > 1) {
count += (f * (f - 1)) / 2;
}
}
return count;
}
public static void main(String[] args) {
String s = "xyyx";
System.out.println(countAnagramPairs(s));
}
}
from collections import defaultdict
def countAnagramPairs(s):
n = len(s)
count = 0
# Map to store frequency of sorted substrings
freqMap = defaultdict(int)
# Generate all substrings
for i in range(n):
curr = ""
for j in range(i, n):
curr += s[j]
# Sort characters of the current substring
sortedStr = ''.join(sorted(curr))
# Increment count of this sorted pattern
freqMap[sortedStr] += 1
# Count total anagrammatic pairs from frequencies
for f in freqMap.values():
# Choose any 2 from f => f * (f - 1) / 2
if f > 1:
count += (f * (f - 1)) // 2
return count
if __name__ == "__main__":
s = "xyyx"
print(countAnagramPairs(s))
using System;
using System.Collections.Generic;
class GfG {
public static int countAnagramPairs(string s) {
int n = s.Length;
int count = 0;
// Map to store frequency of sorted substrings
Dictionary<string, int> freqMap = new Dictionary<string, int>();
// Generate all substrings
for (int i = 0; i < n; i++) {
string curr = "";
for (int j = i; j < n; j++) {
curr += s[j];
// Sort characters of the current substring
char[] chars = curr.ToCharArray();
Array.Sort(chars);
string sortedStr = new string(chars);
// Increment count of this sorted pattern
if (freqMap.ContainsKey(sortedStr)) {
freqMap[sortedStr]++;
} else {
freqMap[sortedStr] = 1;
}
}
}
// Count total anagrammatic pairs from frequencies
foreach (var entry in freqMap) {
int f = entry.Value;
// Choose any 2 from f => f * (f - 1) / 2
if (f > 1) {
count += (f * (f - 1)) / 2;
}
}
return count;
}
public static void Main() {
string s = "xyyx";
Console.WriteLine(countAnagramPairs(s));
}
}
function countAnagramPairs(s) {
let n = s.length;
let count = 0;
// Map to store frequency of sorted substrings
let freqMap = new Map();
// Generate all substrings
for (let i = 0; i < n; i++) {
let curr = "";
for (let j = i; j < n; j++) {
curr += s[j];
// Sort characters of the current substring
let sortedStr = curr.split('').sort().join('');
// Increment count of this sorted pattern
if (freqMap.has(sortedStr)) {
freqMap.set(sortedStr, freqMap.get(sortedStr) + 1);
} else {
freqMap.set(sortedStr, 1);
}
}
}
// Count total anagrammatic pairs from frequencies
for (let f of freqMap.values()) {
// Choose any 2 from f => f * (f - 1) / 2
if (f > 1) {
count += (f * (f - 1)) / 2;
}
}
return count;
}
//Driver Code
let s = "xyyx";
console.log(countAnagramPairs(s));
Output
4
[Expected Approach 2] Using Hash Map - O(n^2) Time and O(n^2) Space
Fix a starting index and expand the substring one character at a time while maintaining a frequency array of size 26. Use this frequency array as a signature (hash key) in a map to count how many times the same pattern has appeared. If a pattern repeats, add its previous frequency to the answer since those substrings form anagram pairs.
#include <iostream>
#include <unordered_map>
using namespace std;
int countAnagramPairs(string s) {
int n = s.length();
int count = 0;
unordered_map<string, int> mp;
for (int i = 0; i < n; i++) {
vector<int> freq(26, 0);
for (int j = i; j < n; j++) {
freq[s[j] - 'a']++;
string key = "";
for (int k = 0; k < 26; k++) {
key += to_string(freq[k]) + "#";
}
count += mp[key];
mp[key]++;
}
}
return count;
}
int main() {
string s = "abba";
cout << countAnagramPairs(s);
return 0;
}
import java.util.*;
public class GFG {
static int countAnagramPairs(String s) {
int n = s.length();
int count = 0;
HashMap<String, Integer> mp = new HashMap<>();
for (int i = 0; i < n; i++) {
int[] freq = new int[26];
for (int j = i; j < n; j++) {
freq[s.charAt(j) - 'a']++;
String key = "";
for (int k = 0; k < 26; k++) {
key += freq[k] + "#";
}
count += mp.getOrDefault(key, 0);
mp.put(key, mp.getOrDefault(key, 0) + 1);
}
}
return count;
}
public static void main(String[] args) {
String s = "abba";
System.out.println(countAnagramPairs(s));
}
}
def countAnagramPairs(s):
n = len(s)
count = 0
mp = {}
for i in range(n):
freq = [0] * 26
for j in range(i, n):
freq[ord(s[j]) - ord('a')] += 1
key = tuple(freq)
count += mp.get(key, 0)
mp[key] = mp.get(key, 0) + 1
return count
s = "abba"
print(countAnagramPairs(s))
using System;
using System.Collections.Generic;
public class GFG
{
static int countAnagramPairs(string s)
{
int n = s.Length;
int count = 0;
Dictionary<string, int> map = new Dictionary<string, int>();
for (int i = 0; i < n; i++)
{
int[] freq = new int[26];
for (int j = i; j < n; j++)
{
freq[s[j] - 'a']++;
string key = string.Join(",", freq);
if (map.ContainsKey(key))
{
count += map[key];
map[key]++;
}
else
{
map[key] = 1;
}
}
}
return count;
}
public static void Main()
{
string s = "abba";
Console.WriteLine(countAnagramPairs(s));
}
}
function countAnagramPairs(s) {
let n = s.length;
let count = 0;
let map = new Map();
for (let i = 0; i < n; i++) {
let freq = new Array(26).fill(0);
for (let j = i; j < n; j++) {
freq[s.charCodeAt(j) - 97]++;
let key = freq.join(",");
if (map.has(key)) {
count += map.get(key);
map.set(key, map.get(key) + 1);
} else {
map.set(key, 1);
}
}
}
return count;
}
let s = "abba";
console.log(countAnagramPairs(s));
Output
4