Naive solution based on the former question
commonPrefix
is supposed to (according to the comment) the longest prefix in the array up to index n. What does that mean? You need to calculate all prefixes and pick the longest.
static String commonPrefix(String arr[], int n) {
String longestPrefix = "";
for (int i = 0; i < n; i++) {
final String currentPrefix = commonPrefixUtil(arr[i], arr[n]);
if (currentPrefix.length() > longestPrefix.length()) {
longestPrefix = currentPrefix;
}
}
return longestPrefix;
}
So that would yield "00"
for arr = ["000", "1110", "01", "001", "110", "11"]; n = 3
.
Now that we have the longest prefix, what? We need to find the closest index to n
that starts with that prefix...
static int closestIndex(String[] arr, String longestPrefix, int n) {
for (int i = n - 1; i >= 0; i--) {
if (arr[i].startsWith(longestPrefix)) {
return i + 1; // + 1 because the solution wants starting index with 1
}
}
return 0;
}
How to put it together? Just call the two methods for each input
public static void main(String[] args) {
String[] words = { "000", "1110", "01", "001", "110", "11" };
int[] output = new int[words.length];
for (int i = 0; i < words.length; i++) {
final String longestPrefix = commonPrefix(words, i);
output[i] = closestIndex(words, longestPrefix, i);
}
System.out.println(Arrays.toString(output));
}
You have deleted your commonPrefixUtil
implementation that worked from the question, so I add my own:
static String commonPrefixUtil(String str1, String str2) {
int shorterStringLength = Math.min(str1.length(), str2.length());
int length = 0;
for (; length < shorterStringLength; length++) {
if (str1.charAt(length) != str2.charAt(length)) {
break;
}
}
return str1.substring(0, length);
}
Optimalized solution
I created a new solution using dynamic programming with tabulation (if I understand correctly), that is I use a hashmap of already all prefixes pointing to the indexes of the words where the prefix is from. The value of the Map is a sorted tree, so it can be really easily determined which word with the common prefix is closest to the current index. The HashMap ensures constant-time operations and the TreeSet guarantees log(n) time cost.
Explained more simply, I process the first letter of all words, and then the next etc. During this process I memorise where all prefix substrings are, while they are automatically sorted. I stop the loop after processing the last letter of the longest word.
public static void main(String[] args) {
String[] words = { "000", "1110", "01", "001", "110", "11" };
int[] result = new int[words.length];
final int firstWordLength = words.length > 0 ? words[0].length() : 8;
// prefix -> [indexes of prefix occurrence]
HashMap<String, TreeSet<Integer>> prefixes = new HashMap<>(words.length * (firstWordLength + 1) * 2);
int wordLength = 1;
boolean isUpdatedResult;
do { // O(k)
isUpdatedResult = false;
for (int wordIdx = 0; wordIdx < words.length; wordIdx++) { // O(n)
if (words[wordIdx].length() < wordLength) {
continue;
}
final String currentPrefix = words[wordIdx].substring(0, wordLength); // Java >= 7 update 6 ? O(k) : O(1)
final TreeSet<Integer> prefixIndexes = prefixes.get(currentPrefix); // O(1)
if (prefixIndexes != null) {
// floor instead of lower, because we have put `wordIdx + 1` inside
final Integer closestPrefixIndex = prefixIndexes.floor(wordIdx); // O(log n)
if (closestPrefixIndex != null) {
result[wordIdx] = closestPrefixIndex;
isUpdatedResult = true;
}
}
// take the previous index for the result if no match
if (result[wordIdx] == 0) {
result[wordIdx] = wordIdx;
}
final TreeSet<Integer> newPrefixIndexes = prefixes.computeIfAbsent(currentPrefix, p -> new TreeSet<>()); // O(1)
// the result index must be + 1
newPrefixIndexes.add(wordIdx + 1); // O(log n)
}
wordLength++;
} while (isUpdatedResult);
System.out.println(Arrays.toString(result));
}
I have marked all the operations with their big O time complexity. n
is the number of words in the input array, i.e. words.length
and k
is the length of the longest word. According to Jon Skeet's post the time complexity of substring has changed in Java 7 update 6 to linear.
So we can calculate:
O(k) * O(n) * (O(log n) + O(k))
Hopefully, the code is understandable and I calculated the complexity correctly.