Given three strings, S1, S2, and S3. The characters at the same index in S1 and S2 are considered equivalent and follow the transitive property, the task is to find and print the lexicographically smallest string that you can obtain from S3 by changing its characters to some other equivalent character obtained from S1 and S2. In other words, you need to replace the characters in S3 with their equivalent characters from S1 and S2 while maintaining the lexicographic order.
Examples:
Input: S1 = “abc” , S2 = “xyz” , S3 =” xazy”
Output: aacb
Explanation: ‘x’ replaced by ‘a’, ‘y’ is replaced by ‘b’ and ‘z’ is replaced by ‘c’Input: S1 = “abc” , S2 = “xcd” , S3 =” xdc”
Output: abb
Explanation: {a, x} are equivalent, and {b, c, d} are equivalent, so x is replaced with a, d is replaced with b, and c is replaced with b.
Naïve approach: We can solve this problem using the below idea:
- Create a graph where s1[i] is connected with s2[i].
-
then traverse string s3 from left to right and do the following.
- Call DFS for the s3[i] node and calculate the minimum character we can reach from node s3[i].
- assign this minimum character to s3[i].
- return string s3.
Time Complexity: O(N2)
Auxiliary Space: O ( 1 )
Efficient Approach: We can solve this problem optimally using the below idea:
Idea is to use the DSU {Disjoint Set Union}. We can connect the two characters which are mapped with each other and at the end we traverse the s3 string and assign every s3[i] to the parent of s3[i].
Follow the steps to solve the problem:
- Make a node for every character.
- Traverse in the string s1 and do Union of s1[i] with s2[i].
- When all Union operation is being done.
- Traverse in the s3 string and for every character of s3[i], assign it to its parent.
- Return the s3 string.
Below is the C++ implementation of the above idea :
// C++ code for the above approach: #include <bits/stdc++.h> using namespace std;
// Parent array int parent[1000];
// Size array int size[1000];
void make( int n)
{ parent[n] = n;
size[n] = 1;
} // Find function int find( int n)
{ if (n == parent[n])
return n;
return parent[n] = find(parent[n]);
} // Union function void Union( int a, int b)
{ a = find(a);
b = find(b);
// Union of two components which
// are not connected
if (a != b) {
// If a is greater than b so we swap
// them to make a always smaller and
// we have made a as parent of
// that component
if (a > b)
swap(a, b);
parent[b] = a;
size[a] += size[b];
}
} string solve(string s1, string s2, string s3) { // Creating nodes for every characters
for ( char ch = 'a' ; ch <= 'z' ; ch++)
make(ch);
// Doing union of s1[i] and s2[i]
for ( int i = 0; i < s1.length(); i++) {
Union(s1[i], s2[i]);
}
// Finding and assigning the smallest
// value to s3[i]
for ( int i = 0; i < s3.length(); i++) {
s3[i] = find(s3[i]);
}
// Return the s3 string
return s3;
} // Driver Code int main()
{ string s1, s2, s3;
s1 = "abc" ;
s2 = "xcd" ;
s3 = "xdc" ;
// Function call
cout << solve(s1, s2, s3);
return 0;
} |
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class GFG {
private Map<Character, Character> parent;
private Map<Character, Integer> size;
public GFG() {
parent = new HashMap<>();
size = new HashMap<>();
}
private void make(Character n) {
parent.put(n, n);
size.put(n, 1 );
}
private Character find(Character n) {
if (n.equals(parent.get(n)))
return n;
return parent.put(n, find(parent.get(n)));
}
private void Union(Character a, Character b) {
a = find(a);
b = find(b);
if (!a.equals(b)) {
if (a > b) {
Character temp = a;
a = b;
b = temp;
}
parent.put(b, a);
size.put(a, size.get(a) + size.get(b));
}
}
private String solve(String s1, String s2, String s3) {
for ( char ch = 'a' ; ch <= 'z' ; ch++) {
make(ch);
}
for ( int i = 0 ; i < s1.length(); i++) {
Union(s1.charAt(i), s2.charAt(i));
}
StringBuilder result = new StringBuilder();
for ( int i = 0 ; i < s3.length(); i++) {
result.append(find(s3.charAt(i)));
}
return result.toString();
}
public static void main(String[] args) {
GFG dsu = new GFG();
String s1 = "abc" ;
String s2 = "xcd" ;
String s3 = "xdc" ;
// Function call
System.out.println(dsu.solve(s1, s2, s3));
}
} |
# Python code for the above approach: # Parent dictionary parent = {}
# Size dictionary size = {}
def make(n):
parent[n] = n
size[n] = 1
# Find function def find(n):
if n = = parent[n]:
return n
parent[n] = find(parent[n])
return parent[n]
# Union function def Union(a, b):
a = find(a)
b = find(b)
# Union of two components which
# are not connected
if a ! = b:
# If a is greater than b so we swap
# them to make a always smaller and
# we have made a as parent of
# that component
if a > b:
a, b = b, a
parent[b] = a
size[a] + = size[b]
def solve(s1, s2, s3):
# Creating nodes for every characters
for ch in range ( ord ( 'a' ), ord ( 'z' ) + 1 ):
make( chr (ch))
# Doing union of s1[i] and s2[i]
for i in range ( len (s1)):
Union(s1[i], s2[i])
# Finding and assigning the smallest
# value to s3[i]
new_s3 = ""
for i in range ( len (s3)):
new_s3 + = find(s3[i])
# Return the modified s3 string
return new_s3
# Driver Code if __name__ = = "__main__" :
s1 = "abc"
s2 = "xcd"
s3 = "xdc"
# Function call
print (solve(s1, s2, s3))
# This code is contributed by Sakshi |
// C# code for the above approach: using System;
class UnionFind
{ private int [] parent;
private int [] size;
public UnionFind( int n)
{
// Initialize parent and size arrays
parent = new int [n];
size = new int [n];
for ( int i = 0; i < n; i++)
{
// Initially, each element is its own parent
parent[i] = i;
// Size of each component is 1 at the beginning
size[i] = 1;
}
}
public int Find( int n)
{
// Find the root of the component containing 'n'
if (n == parent[n])
return n;
// Path compression: Update the parent to the root
return parent[n] = Find(parent[n]);
}
public void Union( int a, int b)
{
a = Find(a);
b = Find(b);
if (a != b)
{
if (a > b)
Swap( ref a, ref b);
// Union of two components by making 'a' the parent
parent[b] = a;
// Update the size of the component 'a'
size[a] += size[b];
}
}
private static void Swap( ref int a, ref int b)
{
// Helper function to swap two integers
int temp = a;
a = b;
b = temp;
}
} class Program
{ static string Solve( string s1, string s2, string s3)
{
int n = 26; // Number of characters (a to z)
UnionFind uf = new UnionFind(n);
// Union operation for corresponding characters in s1 and s2
for ( int i = 0; i < s1.Length; i++)
{
uf.Union(s1[i] - 'a' , s2[i] - 'a' );
}
char [] result = new char [s3.Length];
for ( int i = 0; i < s3.Length; i++)
{
// Find the representative character for each character in s3
result[i] = ( char )( 'a' + uf.Find(s3[i] - 'a' ));
}
// Return the resulting string
return new string (result);
}
static void Main()
{
string s1 = "abc" ;
string s2 = "xcd" ;
string s3 = "xdc" ;
// Call the Solve function and print the result
string result = Solve(s1, s2, s3);
Console.WriteLine(result);
}
} // this code is contributed by uttamdp_10 |
// Javascript code for the above approach: // Parent dictionary (using JavaScript objects) const parent = {}; // Size dictionary (using JavaScript objects) const size = {}; function make(n) {
parent[n] = n;
size[n] = 1;
} function find(n) {
if (n === parent[n]) {
return n;
}
parent[n] = find(parent[n]);
return parent[n];
} function Union(a, b) {
a = find(a);
b = find(b);
if (a !== b) {
if (a > b) {
[a, b] = [b, a]; // Swap a and b
}
parent[b] = a;
size[a] += size[b];
}
} function solve(s1, s2, s3) {
// Creating nodes for every characters
for (let ch = 'a' .charCodeAt(0); ch <= 'z' .charCodeAt(0); ch++) {
make(String.fromCharCode(ch));
}
// Doing union of s1[i] and s2[i]
for (let i = 0; i < s1.length; i++) {
Union(s1.charAt(i), s2.charAt(i));
}
// Finding and assigning the smallest
// value to s3[i]
let new_s3 = "" ;
for (let i = 0; i < s3.length; i++) {
new_s3 += find(s3.charAt(i));
}
// Return the modified s3 string
return new_s3;
} // Driver Code const s1 = "abc" ;
const s2 = "xcd" ;
const s3 = "xdc" ;
// Function call console.log(solve(s1, s2, s3)); |
abb
Time Complexity: O(N)
Auxiliary Space: O(1), because the number of nodes can never exceed 26 (number of alphabets).