Given an array of strings (all lowercase letters), the task is to group them in such a way that all strings in a group are shifted versions of each other.
Two strings s1 and s2 are called shifted if the following conditions are satisfied:
- s1.length is equal to s2.length
- s1[i] is equal to s2[i] + m for all 1 <= i <= s1.length for a constant integer m. Consider the shifting to be cyclic, that is if s2[i] + m > 'z', then start from 'a' or if s2[i] + m < 'a', then start from 'z'.
Examples:
Input: arr[] = ["acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs"]
Output: [ ["acd", "dfg", "wyz", "yab", "mop"], ["bdfh", "moqs"], ["a", "x"] ]
Explanation: All shifted strings are grouped together.
Input: arr = ["geek", "for", "geeks"]
Output: [["for"], ["geek"], ["geeks"]]
Approach: Using Hash Map
The idea is to generate a unique hash for each group by normalizing the strings. Here normalizing means making the first character of every string equal to 'a' by calculating the required shifts and applying it uniformly to all characters in cyclic fashion.
Example: s = "dca", shifts = 'd' - 'a' = 3
normalized characters: 'd' - 3 = 'a', 'c' - 3 = 'z' and 'a' - 3 = 'x'
normalized string = "azx"
The normalized string (hash) represents the shift pattern such that strings with the same pattern have the same hash. We use a hash map to track these hashes, and map them to corresponding groups. For each string, we compute a hash and use it to either create a new group or add the string to an existing group in a single traversal.
C++
// C++ program to print groups of shifted strings
// together using Hash Map
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
// Function to generate hash by shifting and equating
// the first character
string getHash(string s) {
// Calculate the shift needed to normalize the string
int shifts = s[0] - 'a';
for (char &ch : s) {
// Adjust each character by the shift
ch = ch - shifts;
// Wrap around if the character goes below 'a'
if (ch < 'a')
ch += 26;
}
return s;
}
// Function to group shifted string together
vector<vector<string>> groupShiftedString(vector<string> &arr) {
vector<vector<string>> res;
// Maps hash to index in result
unordered_map<string, int> mp;
for (string s : arr) {
// Generate hash representing the shift pattern
string hash = getHash(s);
// If new hash, create a new group
if (mp.find(hash) == mp.end()) {
mp[hash] = res.size();
res.push_back({});
}
// Add string to its group
res[mp[hash]].push_back(s);
}
return res;
}
int main() {
vector<string> arr = {"acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs"};
vector<vector<string>> groups = groupShiftedString(arr);
for (vector<string> &group : groups) {
for (string &s : group) {
cout << s << " ";
}
cout << endl;
}
return 0;
}
Java
// Java program to print groups of shifted strings
// together using Hash Map
import java.util.*;
class GfG {
// Function to generate hash by shifting and equating the
// first character
static String getHash(String s) {
// Calculate the shift needed to normalize the string
int shifts = s.charAt(0) - 'a';
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
// Adjust each character by the shift
chars[i] = (char) (chars[i] - shifts);
// Wrap around if the character goes below 'a'
if (chars[i] < 'a')
chars[i] += 26;
}
return new String(chars);
}
// Function to group shifted strings together
static ArrayList<ArrayList<String>> groupShiftedString(String[] arr) {
ArrayList<ArrayList<String>> res = new ArrayList<>();
// Maps hash to index in result
HashMap<String, Integer> mp = new HashMap<>();
for (String s : arr) {
// Generate hash representing the shift pattern
String hash = getHash(s);
// If new hash, create a new group
if (!mp.containsKey(hash)) {
mp.put(hash, res.size());
res.add(new ArrayList<>());
}
// Add string to its group
res.get(mp.get(hash)).add(s);
}
return res;
}
public static void main(String[] args) {
String[] arr = {"acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs"};
ArrayList<ArrayList<String>> groups = groupShiftedString(arr);
for (ArrayList<String> group : groups) {
for (String s : group) {
System.out.print(s + " ");
}
System.out.println();
}
}
}
Python
# Python program to print groups of shifted strings
# together using Hash Map
# Function to generate hash by shifting and equating the first character
def getHash(s):
# Calculate the shift needed to normalize the string
shifts = ord(s[0]) - ord('a')
hashVal = []
for ch in s:
# Adjust each character by the shift
newCh = chr(ord(ch) - shifts)
# Wrap around if the character goes below 'a'
if newCh < 'a':
newCh = chr(ord(newCh) + 26)
hashVal.append(newCh)
return ''.join(hashVal)
# Function to group shifted strings together
def groupShiftedString(arr):
res = []
# Maps hash to index in result
mp = {}
for s in arr:
# Generate hash representing the shift pattern
hashVal = getHash(s)
# If new hash, create a new group
if hashVal not in mp:
mp[hashVal] = len(res)
res.append([])
# Add string to its group
res[mp[hashVal]].append(s)
return res
if __name__ == "__main__":
arr = ["acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs"]
groups = groupShiftedString(arr)
for group in groups:
print(" ".join(group))
C#
// C# program to print groups of shifted strings
// together using Hash Map
using System;
using System.Collections.Generic;
class GfG {
// Function to generate hash by shifting and equating the first character
static string getHash(string s) {
// Calculate the shift needed to normalize the string
int shifts = s[0] - 'a';
char[] chars = s.ToCharArray();
for (int i = 0; i < chars.Length; i++) {
// Adjust each character by the shift
chars[i] = (char)(chars[i] - shifts);
// Wrap around if the character goes below 'a'
if (chars[i] < 'a')
chars[i] += (char)26;
}
return new string(chars);
}
// Function to group shifted strings together
static List<List<string>> groupShiftedString(string[] arr) {
List<List<string>> res = new List<List<string>>();
// Maps hash to index in result
Dictionary<string, int> mp = new Dictionary<string, int>();
foreach (string s in arr) {
// Generate hash representing the shift pattern
string hash = getHash(s);
// If new hash, create a new group
if (!mp.ContainsKey(hash)) {
mp[hash] = res.Count;
res.Add(new List<string>());
}
// Add string to its group
res[mp[hash]].Add(s);
}
return res;
}
static void Main(string[] args) {
string[] arr = { "acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs" };
var groups = groupShiftedString(arr);
foreach (var group in groups) {
Console.WriteLine(string.Join(" ", group));
}
}
}
JavaScript
// JavaScript program to print groups of shifted strings
// together using Hash Map
// Function to generate hash by shifting and equating the first character
function getHash(s) {
// Calculate the shift needed to normalize the string
const shifts = s.charCodeAt(0) - 'a'.charCodeAt(0);
let chars = [];
for (let ch of s) {
// Adjust each character by the shift
let newChar = String.fromCharCode(ch.charCodeAt(0) - shifts);
// Wrap around if the character goes below 'a'
if (newChar < 'a')
newChar = String.fromCharCode(newChar.charCodeAt(0) + 26);
chars.push(newChar);
}
return chars.join('');
}
// Function to group shifted strings together
function groupShiftedString(arr) {
const res = [];
// Maps hash to index in result
const mp = new Map();
for (let s of arr) {
// Generate hash representing the shift pattern
const hash = getHash(s);
// If new hash, create a new group
if (!mp.has(hash)) {
mp.set(hash, res.length);
res.push([]);
}
// Add string to its group
res[mp.get(hash)].push(s);
}
return res;
}
// Driver Code
const arr = ["acd", "dfg", "wyz", "yab", "mop", "bdfh", "a", "x", "moqs"];
const groups = groupShiftedString(arr);
groups.forEach(group => console.log(group.join(" ")));
Outputacd dfg wyz yab mop
bdfh moqs
a x
Time Complexity: O(n*k), where n is the length of string array and k is maximum length of a string in string array.
Auxiliary Space: O(n*k), in worst case we might generate n different hash strings respectively for each input string. So we got n different entries in hash map each of length k or less.
Similar Reads
C# Strings In C#, a string is a sequence of Unicode characters or an array of characters. The range of Unicode characters will be U+0000 to U+FFFF. The array of characters is also termed as the text. So the string is the representation of the text. A string is an important concept, and sometimes people get con
7 min read
C# String Join Method | Set - 2 The C# Join() method is a method that is present in the String class. This method is used to concatenate or combine different kinds of collections, such as an array, list or set. With the specified separator between each member or element. Key Features:Concatenation: The Join() method is used to com
4 min read
Group Anagrams Together Given an array of words arr[], the task is to groups strings that are anagrams. An anagram is a word or phrase formed by rearranging the letters of another, using all the original letters exactly once.Example:Input: arr[] = ["act", "god", "cat", "dog", "tac"]Output: [["act", "cat", "tac"], ["god", "
7 min read
POTD Solutions | 15 Novâ 23 | Better String Given a pair of strings s1 and s2 of equal lengths, your task is to find which of the two strings has more distinct subsequences. If both strings have the same number of distinct subsequences, return s1.Examples:Input: s1 = "gfg", s2 = "ggg"Output: "gfg"Explanation: "gfg" have 6 distinct subsequence
12 min read
Group consecutive characters of same type in a string Given a string str consisting of upper case alphabets, numeric digits and arithmetic operators, the task is to group them into continuous sub-strings of the same type.Examples: Input: str = "G E E K S 1 2 3 4 5" Output: GEEKS 12345 All contiguous upper case characters can be grouped together and all
8 min read