package com.interview.dynamic;
import java.util.HashMap;
import java.util.Map;
/**
* Read question at https://leetcode.com/problems/scramble-string/
*/
public class ScrambledString {
/**
* Index for memoization.
*/
private static class Index {
int start1;
int end1;
int start2;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Index index = (Index) o;
if (start1 != index.start1) return false;
if (end1 != index.end1) return false;
if (start2 != index.start2) return false;
return end2 == index.end2;
}
@Override
public int hashCode() {
int result = start1;
result = 31 * result + end1;
result = 31 * result + start2;
result = 31 * result + end2;
return result;
}
int end2;
}
boolean isScrambled(char input1[], char input2[], int start1, int end1, int start2, int end2, Map<Index, Boolean> memMap) {
//return false conditions
if(start1 > end1 || start2 > end2) {
return false;
}
if(end1 - start1 != end2 - start2) {
return false;
}
Index index = new Index();
index.start1 = start1;
index.end1 = end1;
index.start2 = start2;
index.end2 = end2;
//if we have already calculated value for index then lets use it instead of recalculating again.
if(memMap.containsKey(index)) {
return memMap.get(index);
}
//if both input from their respective start to end are same then return true.
boolean isSame = true;
for(int i= start1, j = start2; i <= end1 && j <= end2; i++, j++) {
if(input1[i] != input2[j]) {
isSame = false;
break;
}
}
if(isSame) {
memMap.put(index, true);
return true;
}
//check if both input from their respective start to end have same characters. If not then return false.
Map<Character, Integer> countMap = new HashMap<>();
for(int i= start1; i <= end1; i++) {
countMap.compute(input1[i], (ch, val) -> {
if(val == null) {
return 1;
} else {
return val + 1;
}
});
}
for(int i= start2; i <= end2; i++) {
countMap.compute(input2[i], (ch, val) -> {
if(val == null) {
return -1;
} else {
return val - 1;
}
});
}
//all values in map should have value 0 otherwise there is character mismatch. Return false in that case.
long nonZeroCount = countMap.values().stream().filter(val -> val !=0 ).count();
if(nonZeroCount > 0) {
memMap.put(index, false);
return false;
}
//for values from input range try splitting into 2 and check recursively if they match or not.
for(int len = 0; len < end1 - start1; len++) {
//e.g great gtear so match g ,g and reat, tear
if(isScrambled(input1, input2, start1, start1 + len, start2, start2 + len, memMap) &&
isScrambled(input1, input2, start1 + len +1, end1, start2 + len + 1, end2, memMap)) {
memMap.put(index, true);
return true;
}
//e.g great reatg so match g,g and reat,reat
if(isScrambled(input1, input2, start1, start1 + len, end2 - len, end2, memMap) &&
isScrambled(input1, input2, start1 + len +1, end1, start2, end2 - len -1, memMap)) {
memMap.put(index, true);
return true;
}
}
memMap.put(index, false);
return false;
}
public static void main(String args[]) {
ScrambledString ss = new ScrambledString();
String str1 = "great";
String str2 = "rgtae";
Map<Index, Boolean> memMap = new HashMap<>();
boolean result = ss.isScrambled(str1.toCharArray(), str2.toCharArray(), 0, str1.length() - 1, 0, str2.length() -1, memMap);
System.out.println(result);
}
}