Given two strings s1 and s2. Find the minimum number of steps required to transform string s1 into string s2. The only allowed operation for the transformation is selecting a character from string s1 and inserting it in the beginning of string s2.
Examples:
Input: s1 = "abd", s2 = "bad"
Output: 1
Explanation: The conversion can take place in 1 operation: Pick 'b' and place it at the front.Input: s1 = "GeeksForGeeks" s2 = "ForGeeksGeeks"
Output: 3
Explanation: The conversion can take place in 3 operations:
Pick 'r' and place it at the front, A = "rGeeksFoGeeks"
Pick 'o' and place it at the front, A = "orGeeksFGeeks"
Pick 'F' and place it at the front, A = "ForGeeksGeeks"
Using Hash Map (or Dictionary) and Two Pointers - O(n) Time
The transformation is only possible if both strings have the same characters with the exact same frequencies. use a HashMap (or a frequency array) to count the characters in string s1 and then "cancel them out" using string s2.
If frequencies are same, then begin matching from end of both strings.
- Keep moving both pointers while there is a match
- For mismatches, increment result and move only pointer of s1 as the current substring of s1 can match with current prefix of s2.
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
int transform(string &s1, string &s2) {
if (s1.length() != s2.length()) {
return -1;
}
// Create a map to store the frequency of characters in string s1
unordered_map<char, int> m;
int n = s1.length();
for (int i = 0; i < n; i++) {
// If the character already exists in the map
if (m.count(s1[i]))
m[s1[i]]++;
// Add the character to the map with a frequency of 1
else
m[s1[i]] = 1;
}
// Subtract the frequency of characters in string s2 from the map
for (int i = 0; i < n; i++) {
if (m.count(s2[i]))
m[s2[i]]--;
}
// Check if all frequencies are 0 to verify strings are anagrams
for (auto it : m) {
if (it.second != 0)
return -1;
}
// Calculate minimum operations using a greedy right-to-left approach
int i = n - 1, j = n - 1;
int res = 0;
while (i >= 0 && j >= 0) {
// If characters mismatch, s1[i] must be moved to the front
while (i >= 0 && s1[i] != s2[j]) {
// Increment the number of operations required
res++;
// Move pointer i to the left to find a match for s2[j]
i--;
}
// Move both pointers if a match is found or i is exhausted
i--;
j--;
}
return res;
}
int main() {
string s1 = "GeeksForGeeks";
string s2 = "ForGeeksGeeks";
cout << transform(s1, s2) << endl;
return 0;
}
import java.util.HashMap;
import java.util.Map;
class GFG {
public static int transform(String s1, String s2) {
// If lengths differ, transformation is impossible
if (s1.length() != s2.length()) {
return -1;
}
// Step 1: Character Frequency Check (Anagram Check)
// We use a HashMap to ensure both strings contain the exact same characters.
HashMap<Character, Integer> m = new HashMap<Character, Integer>();
int n = s1.length();
// Count characters in string s1
for (int i = 0; i < n; i++) {
m.put(s1.charAt(i), m.getOrDefault(s1.charAt(i), 0) + 1);
}
// Subtract character counts based on string s2
for (int i = 0; i < n; i++) {
if (m.containsKey(s2.charAt(i)))
m.put(s2.charAt(i), m.get(s2.charAt(i)) - 1);
}
// If any count is non-zero, strings aren't anagrams; return -1
for (Map.Entry<Character, Integer> entry : m.entrySet()) {
if (entry.getValue() != 0)
return -1;
}
// Step 2: Greedy Two-Pointer Approach
// Since we can only move to the FRONT, we match characters starting from the BACK (right to left).
int i = n - 1, j = n - 1;
int res = 0;
while (i >= 0 && j >= 0) {
// If characters at current pointers don't match, s1.charAt(i)
// is a character that must be moved to the front eventually.
while (i >= 0 && s1.charAt(i) != s2.charAt(j)) {
res++; // This character 'i' will be moved, increment operations
i--; // Move 'i' left to find the next potential match for s2[j]
}
// If we found a match (s1[i] == s2[j]), move both pointers left to check the next set
if (i >= 0) {
i--;
j--;
}
}
return res;
}
public static void main(String[] args) {
String s1 = "GeeksForGeeks";
String s2 = "ForGeeksGeeks";
System.out.println(transform(s1, s2));
}
}
def transform(s1, s2):
if len(s1) != len(s2):
return -1
# Step 1: Anagram Check using a frequency dictionary
m = {}
n = len(s1)
# Count frequencies in string s1
for i in range(n):
if s1[i] in m:
m[s1[i]] += 1
else:
m[s1[i]] = 1
# Subtract frequencies based on string s2
for i in range(n):
if s2[i] in m:
m[s2[i]] -= 1
else:
# If a character in s2 isn't in s1 at all
return -1
# If any frequency is not zero, the strings cannot be transformed
for key in m:
if m[key] != 0:
return -1
# Step 2: Greedy Two-Pointer Approach
# We move from right to left to find characters already in their target positions
i, j = n - 1, n - 1
res = 0
while i >= 0 and j >= 0:
# If s1[i] doesn't match s2[j], it must be moved to the front eventually
while i >= 0 and s1[i] != s2[j]:
res += 1
# Move i to the left to check the next available character
i -= 1
# If s1[i] matches s2[j], we move both pointers left to find the next match
i -= 1
j -= 1
return res
if __name__ == "__main__":
s1 = "GeeksForGeeks"
s2 = "ForGeeksGeeks"
print(transform(s1, s2))
using System;
using System.Collections.Generic;
class GFG {
public static int transform(string s1, string s2) {
// First, check if the transformation is even possible based on length
if (s1.Length != s2.Length) {
return -1;
}
// Use a dictionary to track character frequencies for an anagram check
Dictionary<char, int> m = new Dictionary<char, int>();
int n = s1.Length;
// Count each character in string s1
for (int i = 0; i < n; i++) {
if (m.ContainsKey(s1[i])) {
m[s1[i]]++;
}
else {
m[s1[i]] = 1;
}
}
// Subtract frequencies based on string s2
for (int i = 0; i < n; i++) {
if (m.ContainsKey(s2[i])) {
m[s2[i]]--;
}
else {
// If a character in s2 doesn't exist in s1, they aren't anagrams
return -1;
}
}
// If any value is not zero, the strings have different character counts
foreach (var entry in m) {
if (entry.Value != 0) {
return -1;
}
}
// Two-pointer greedy approach starting from the end of the strings
int it = n - 1, j = n - 1;
int res = 0;
while (it >= 0 && j >= 0) {
// If characters don't match, s1[it] must be moved to the front
while (it >= 0 && s1[it] != s2[j]) {
res++;
it--;
}
// If we found a match, move both pointers to the left
if (it >= 0) {
it--;
j--;
}
}
return res;
}
public static void Main(string[] args) {
string s1 = "GeeksForGeeks";
string s2 = "ForGeeksGeeks";
Console.WriteLine(transform(s1, s2));
}
}
function transform(s1, s2) {
if (s1.length !== s2.length) {
return -1;
}
// Step 1: Anagram Check using a frequency object
const m = {};
const n = s1.length;
// Count frequencies in string s1
for (let i = 0; i < n; i++) {
if (m[s1[i]]) {
m[s1[i]]++;
} else {
m[s1[i]] = 1;
}
}
// Subtract frequencies based on string s2
for (let i = 0; i < n; i++) {
if (m[s2[i]]) {
m[s2[i]]--;
} else if (m[s2[i]] === undefined) {
// Character in s2 does not exist in s1
return -1;
}
}
// If any frequency is not zero, the strings are not anagrams
for (const char in m) {
if (m[char] !== 0) {
return -1;
}
}
// Step 2: Greedy Two-Pointer Approach
// We scan from right to left because we can only move to the front
let i = n - 1;
let j = n - 1;
let res = 0;
while (i >= 0 && j >= 0) {
// If characters don't match, s1[i] is out of place and must move
while (i >= 0 && s1[i] !== s2[j]) {
res++;
// Skip this character in s1 and count it as a "move to front"
i--;
}
// If they match, move both pointers to check the next pair
if (i >= 0) {
i--;
j--;
}
}
return res;
}
// Driver code
const s1 = "GeeksForGeeks";
const s2 = "ForGeeksGeeks";
console.log(transform(s1, s2));
Output
3
Note : We can further optimize the above approach by using a count array of size 256 instead of map.