Input: text ="thestoryofleetcodeandme", words =["story","fleet","leetcode"]Output: [[3,7],[9,13],[10,17]]
Example 2:
1
2
3
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].
Constraints:
1 <= text.length <= 100
1 <= words.length <= 20
1 <= words[i].length <= 50
text and words[i] consist of lowercase English letters.
We want to efficiently find all substrings of text that match any word in words. A Trie allows us to quickly check for any word starting at each position in text.
structTrieNode {
TrieNode* nxt[26] = {};
bool end = false;
};
classSolution {
public: vector<vector<int>> indexPairs(string text, vector<string>& words) {
TrieNode* root =new TrieNode();
for (auto& w : words) {
TrieNode* cur = root;
for (char c : w) {
int i = c -'a';
if (!cur->nxt[i]) cur->nxt[i] =new TrieNode();
cur = cur->nxt[i];
}
cur->end = true;
}
vector<vector<int>> ans;
int n = text.size();
for (int i =0; i < n; ++i) {
TrieNode* cur = root;
for (int j = i; j < n; ++j) {
int idx = text[j] -'a';
if (!cur->nxt[idx]) break;
cur = cur->nxt[idx];
if (cur->end) ans.push_back({i, j});
}
}
return ans;
}
};
classTrieNode {
TrieNode[] nxt =new TrieNode[26];
boolean end =false;
}
classSolution {
publicint[][]indexPairs(String text, String[] words) {
TrieNode root =new TrieNode();
for (String w : words) {
TrieNode cur = root;
for (char c : w.toCharArray()) {
int i = c -'a';
if (cur.nxt[i]==null) cur.nxt[i]=new TrieNode();
cur = cur.nxt[i];
}
cur.end=true;
}
List<int[]> ans =new ArrayList<>();
int n = text.length();
for (int i = 0; i < n; ++i) {
TrieNode cur = root;
for (int j = i; j < n; ++j) {
int idx = text.charAt(j) -'a';
if (cur.nxt[idx]==null) break;
cur = cur.nxt[idx];
if (cur.end) ans.add(newint[]{i, j});
}
}
return ans.toArray(newint[0][]);
}
}
classTrieNode {
val nxt = Array<TrieNode?>(26) { null }
var end = false}
classSolution {
funindexPairs(text: String, words: Array<String>): Array<IntArray> {
val root = TrieNode()
for (w in words) {
var cur = root
for (c in w) {
val i = c - 'a'if (cur.nxt[i] ==null) cur.nxt[i] = TrieNode()
cur = cur.nxt[i]!! }
cur.end = true }
val ans = mutableListOf<IntArray>()
val n = text.length
for (i in0 until n) {
var cur = root
for (j in i until n) {
val idx = text[j] - 'a'if (cur.nxt[idx] ==null) break cur = cur.nxt[idx]!!if (cur.end) ans.add(intArrayOf(i, j))
}
}
return ans.toTypedArray()
}
}
classTrieNode:
def__init__(self):
self.nxt = [None] *26 self.end =FalseclassSolution:
defindexPairs(self, text: str, words: list[str]) -> list[list[int]]:
root = TrieNode()
for w in words:
cur = root
for c in w:
i = ord(c) - ord('a')
ifnot cur.nxt[i]:
cur.nxt[i] = TrieNode()
cur = cur.nxt[i]
cur.end =True ans = []
n = len(text)
for i in range(n):
cur = root
for j in range(i, n):
idx = ord(text[j]) - ord('a')
ifnot cur.nxt[idx]:
break cur = cur.nxt[idx]
if cur.end:
ans.append([i, j])
return ans
structTrieNode {
nxt: [Option<Box<TrieNode>>; 26],
end: bool,
}
impl TrieNode {
fnnew() -> Self {
Self { nxt: Default::default(), end: false }
}
}
fnindex_pairs(text: &str, words: Vec<&str>) -> Vec<(usize, usize)> {
let root = Box::new(TrieNode::new());
for w in words {
letmut cur =&root;
for c in w.chars() {
let i = (c asu8-b'a') asusize;
cur = cur.nxt[i].get_or_insert_with(|| Box::new(TrieNode::new()));
}
cur.end =true;
}
let n = text.len();
let text = text.as_bytes();
letmut ans =vec![];
for i in0..n {
letmut cur =&root;
for j in i..n {
let idx = (text[j] -b'a') asusize;
if cur.nxt[idx].is_none() { break; }
cur = cur.nxt[idx].as_ref().unwrap();
if cur.end { ans.push((i, j)); }
}
}
ans
}
⏰ Time complexity: O(n * L) — n is the length of text, L is the sum of all word lengths (since we may traverse up to the length of the longest word for each position).
🧺 Space complexity: O(L) — For the Trie, where L is the total length of all words.