Index Pairs of a String
By: Omar Faroque
Tags: Leetcode, Interview Questions, Data Structures, Index Pair, Algorithms

Given a text string and words (a list of strings), return all index pairs [i, j] so that the substring text[i]...text[j] is in the list of words .

Example 1:

Input: text = "thestoryofleetcodeandme", words = ["story","fleet","leetcode"]

Output: [[3,7],[9,13],[10,17]]

Example 2:

Input: text = "ababa", words = ["aba","ab"]

Output: [[0,1],[0,2],[2,3],[2,4]]

Explanation:

Notice that matches can overlap, see "aba" is found in [0,2] and [2,4].

Note:

All strings contain only lowercase English letters. It’s guaranteed that all strings in words are different. 1 <= text.length <= 100 1 <= words.length <= 20 1 <= words[i].length <= 50 Return the pairs [i,j] in sorted order (i.e. sort them by their first coordinate in case of ties sort them by their second coordinate).

Solution:

A naive approach would be finding all the match of the word in the text and track the position of the hit.

After finding all the match for every word, do some sorting like interval sorting. I found it tricky.

— Better Approach —

We can use the beauty of TreeMap which does natural sorting for us. We will use the starting position as a Key(K) and value will list(List<Integer>) of endings for all the word. Before creating the output pairs, we can take Key as the first value and Sort the list make pairs with the first value.

Example:

text = "ababa", words = ["aba","ab"]

Take the word “aba” and search it in the text. It will hit the 0th position and 2nd position. so Map would be

K V

0 ->2

2 ->4

Now take “ab”, it will hit 0th and 2nd position. Update our map and it will look like below:

K V

0 ->2,1

2 ->4,3

As we see that our map is naturally ordered already. So take the keys and sort the value before making the pairs.

K V

0 ->1,2

2 ->3,4

So the final result would be:

[0,1][0,2][2,3][2,4]

Here is my java implementation:

public class IndexPairsOfString {

IndexPairsOfString indexPairsOfString;



@BeforeEach

public void init() {

indexPairsOfString = new IndexPairsOfString();

}



@Test

public void firstSimpleExample() {

int[][] result = indexPairsOfString.indexPairs("thestoryofleetcodeandme", new String[]{"story", "fleet", "leetcode"});

Assertions.assertArrayEquals(new int[][]{{3,7},{9,13},{10,17}},result);

} private int[][] indexPairs(String text, String[] words) {

TreeMap<Integer, List<Integer>> map = new TreeMap<>();

for (int i = 0; i < words.length; i++) {

String word = words[i];

List<Integer> startPos = findWord(text, word);

for (int j = 0; j < startPos.size(); j++) {

int s = startPos.get(j);

int e = s + word.length() - 1;

map.computeIfAbsent(s, k -> new ArrayList<>()).add(e);

}

}

System.out.println(map);

ArrayList<int[]> resultList = new ArrayList<>();



Iterator<Map.Entry<Integer, List<Integer>>> iterator = map.entrySet().iterator();

while (iterator.hasNext()) {

Map.Entry<Integer, List<Integer>> entry = iterator.next();

int start = entry.getKey();

List<Integer> val = entry.getValue();

Collections.sort(val);

for (int i = 0; i < val.size(); i++) {

resultList.add(new int[]{start, val.get(i)});

}

}



int[][] res = new int[resultList.size()][2];

for (int i = 0; i < resultList.size(); i++) {

res[i] = resultList.get(i);

}

return res;

}



private List<Integer> findWord(String textString, String word) {

List<Integer> indexes = new ArrayList<Integer>();

String lowerCaseTextString = textString.toLowerCase();

String lowerCaseWord = word.toLowerCase();



int index = 0;

while (index != -1) {

index = lowerCaseTextString.indexOf(lowerCaseWord, index);

if (index != -1) {

indexes.add(index);

index++;

}

}

return indexes;

}

}

You can find the source in GitHub as well.