Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions Problem1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Problem 1 : Group Anagrams

//Approach 1:
//Time Complexity: O(n * k log k), where n is the number of strings and k is the average length of a string
//Space Complexity: O(n * k), where n is the number of strings and k is the average length of a string
class Solution {
/**
* Groups anagrams by sorting each string to create a unique key.
* All anagrams will result in the same sorted string, allowing them to be grouped in a HashMap.
*/
public List<List<String>> groupAnagrams(String[] strs) {

HashMap <String, List<String>> map = new HashMap<>();
for (int i = 0; i < strs.length ; i++){
String currentString = strs[i];
char [] charArray = currentString.toCharArray();
// Sort characters: Anagrams like "eat" and "tea" both become "aet"
Arrays.sort(charArray); // added complexity O(k log k)
String sortedString = String.valueOf(charArray);
// Use the sorted string as a key to group original strings
if(!map.containsKey(sortedString)){
map.put(sortedString, new ArrayList<>());
}
List<String> list = map.get(sortedString);
list.add(currentString);
map.put(sortedString,list);
}
return new ArrayList<>(map.values());
}

}


// Approach 2:

import java.math.BigInteger;

//Time Complexity: O(n * k) where n is the number of strings and k is the average length of a string
//Space Complexity: O(n * k) where n is the number of strings
class Solution {
/**
* Groups anagrams using Prime Factorization (Fundamental Theorem of Arithmetic).
* Each lowercase letter is mapped to a unique prime number. The product of these primes
* for a word is unique to its set of characters, regardless of their order.
*/
public List<List<String>> groupAnagrams(String[] strs) {

HashMap <BigInteger, List<String>> map = new HashMap<>();

for (int i = 0; i < strs.length ; i++){
String currentString = strs[i];
// Calculate a unique numeric key based on prime products
BigInteger primeProduct = primeProduct(currentString);

if(!map.containsKey(primeProduct)){
map.put(primeProduct, new ArrayList<>());
}
List<String> list = map.get(primeProduct);
list.add(currentString);
map.put(primeProduct,list);
}
return new ArrayList<>(map.values());
}

/**
* Calculates the product of primes corresponding to each character in the string.
* Uses BigInteger to prevent overflow for long strings.
*/
private BigInteger primeProduct(String s){
// Mapping 'a' through 'z' to the first 26 prime numbers
int [] prime =new int [] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,103};
BigInteger result = BigInteger.ONE;
for(int i=0;i < s.length();i++){
char ch = s.charAt(i);
// Multiply the running total by the prime number associated with the current character
result = result.multiply(BigInteger.valueOf( prime[ch - 'a']));
}
return result;
}

}
88 changes: 88 additions & 0 deletions Problem2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//Problem 2: Isomorphic Strings

// Approach 1: Using two hashmaps to map characters from one string to another, ensuring one-to-one correspondence.
class Solution {
/**
* Determines if two strings are isomorphic using two HashMaps.
* This approach maintains a bidirectional mapping: one from string 's' characters to 't',
* and another from string 't' characters back to 's'. This ensures a strict 1:1 relationship (bijection),
* preventing multiple characters from mapping to the same character.
*
* Time Complexity: O(n) - We iterate through the strings once, where n is the length of the strings.
* Space Complexity: O(1) - While we use HashMaps, the number of possible characters (like ASCII) is fixed,
* resulting in constant auxiliary space.
*/
public boolean isIsomorphic(String s, String t) {
int sl = s.length();
int tl = t.length();

if(sl != tl){
return false;
}
HashMap<Character,Character> s_map = new HashMap<>();
HashMap<Character,Character> t_map = new HashMap<>();

for(int i = 0 ; i < sl ; i++ ){
Character s_char = s.charAt(i);
Character t_char = t.charAt(i);

if(s_map.containsKey(s_char)){
if(s_map.get(s_char) != t_char){
return false;
}
}else{
s_map.put(s_char, t_char);
}
if(t_map.containsKey(t_char)){
if(t_map.get(t_char) != s_char){
return false;
}
}else{
t_map.put(t_char, s_char);
}
}
return true;
}

// Approach 2: Using HashMap and a HashSet for one-to-one correspondence

/**
* Determines if two strings are isomorphic using one HashMap and one HashSet.
* This approach maps characters from 's' to 't' using a HashMap. To ensure no two characters
* from 's' map to the same character in 't', a HashSet is used to keep track of characters
* in 't' that have already been assigned a mapping.
*
* Time Complexity: O(n) - We iterate through the strings once.
* Space Complexity: O(1) - The space used by the Map and Set is limited by the fixed size of the character set.
*/
public boolean isIsomorphic_2(String s, String t) {
int sl = s.length();
int tl = t.length();

if(sl != tl){
return false;
}
HashMap<Character,Character> s_map = new HashMap<>();
HashSet<Character> t_set = new HashSet<>();

for(int i = 0 ; i < sl ; i++ ){
Character s_char = s.charAt(i);
Character t_char = t.charAt(i);

if(s_map.containsKey(s_char)){
if(s_map.get(s_char) != t_char){
return false;
}
}else{
if(t_set.contains(t_char)){
return false;
}else{
s_map.put(s_char, t_char);
t_set.add(t_char);
}
}

}
return true;
}
}
81 changes: 81 additions & 0 deletions Problem3.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Problem 3: word pattern matching

class Solution {

/**
* Approach 1: Using two hashmaps to map characters to words and vice versa, ensuring one-to-one correspondence.
* Determines if a string follows a given word pattern using two HashMaps.
* This approach maintains a two-way mapping: one from characters to words and another from words to characters.
* This ensures a strict bijection where each unique character maps to exactly one unique word and vice versa.

* Time Complexity: O(n) where n is the length of the string
* Space Complexity: O(n) where n is the length of the string
*/
public boolean wordPattern(String pattern, String s) {
if(pattern == null && s == null) return true;
if(pattern == null || s == null) return false;
String[] tString = s.split(" ");
if(pattern.length() != tString.length)return false;

HashMap<Character, String> smap = new HashMap<>();
HashMap<String,Character > tmap = new HashMap<>();

ArrayList<String> tString1 = new ArrayList<>(Arrays.asList(tString));

for(int i = 0; i < pattern.length(); i++ ){
char sChar = pattern.charAt(i);

if(smap.containsKey(sChar)){
if(!smap.get(sChar).equals( tString[i])) return false;
}else{
smap.put(sChar, tString[i]);
}
if(tmap.containsKey(tString[i])){
if(!tmap.get(tString[i]).equals(sChar)) return false;
}else{
tmap.put(tString[i], sChar);
}

}
return true;
}


// Approach 2: Using HashMap and HashSet for one-to-one correspondence
/**
* Determines if a string follows a given word pattern using a HashMap and a HashSet.
* The HashMap stores the character-to-word mapping. The HashSet tracks words that have already
* been assigned to a character. If a new character maps to a word already in the HashSet,
* it indicates a violation of the bijection.
* Time Complexity: O(n) where n is the length of the string
* Space Complexity: O(n) where n is the length of the string
*/

public boolean wordPattern_2(String pattern, String s) {
if(pattern == null && s == null) return true;
if(pattern == null || s == null) return false;
String[] tString = s.split(" ");
if(pattern.length() != tString.length)return false;

HashMap<Character, String> smap = new HashMap<>();
HashSet<String> tset = new HashSet<>();

for(int i = 0; i < pattern.length(); i++ ){
char sChar = pattern.charAt(i);
String currentWord = tString[i];

if(smap.containsKey(sChar)){
if(!smap.get(sChar).equals(currentWord)){
return false;
}
}else{
if(tset.contains(currentWord)){
return false;
}
}
smap.put(sChar,currentWord);
tset.add(currentWord);
}
return true;
}
}