We have a list of unique strings, and we need to find every ordered pair (i, j) where concatenating words[i] with words[j] produces a palindrome. The pairs are ordered, so (i, j) and (j, i) are different and both can be valid. For instance, "abcd" + "dcba" and "dcba" + "abcd" are both palindromes.
When does concatenating two strings create a palindrome? If words[i] is the exact reverse of words[j], the concatenation is a palindrome. That is not the only case. Take "s" and "lls": the concatenation "slls" is a palindrome even though neither word is the full reverse of the other. Here "s" matches the trailing "s" of "lls", and the leftover part "ll" is itself a palindrome sitting in the middle.
That observation generalizes into a decomposition. Split a word into a prefix and a suffix. If the prefix is a palindrome and the reverse of the suffix exists in the list, that reversed suffix can sit before the word to complete a palindrome. If the suffix is a palindrome and the reverse of the prefix exists in the list, that reversed prefix can sit after the word. These two cases cover every palindrome pair.
1 <= words.length <= 5000 → Checking all n^2 ordered pairs is 25 million pairs, and each pair costs another O(k) to verify, so the brute force is O(n^2 * k). With words up to 300 characters that is too slow, which pushes us toward decomposing each word and looking partners up directly.0 <= words[i].length <= 300 → Words can be empty. An empty string paired with any palindromic word forms a valid pair in both directions, so the empty string is an edge case the decomposition has to handle.words[i] consists of lowercase English letters → The 26-letter alphabet keeps a trie node small (a fixed 26-way branch), which makes the trie approach practical.Translate the problem statement directly: try every ordered pair (i, j) where i != j, concatenate the two words, and check whether the result is a palindrome.
Checking whether a string is a palindrome takes O(k) time where k is the length of the concatenated string, and there are n^2 pairs to check. This is correct but quadratic in the number of words.
(i, j) where i != j, concatenate words[i] + words[j].[i, j] to the result.Loading animation...
The brute force ignores the structure of the strings, comparing every word against every other word. The next approach uses that structure: it decomposes each word and looks up potential partners in a hash map.
Split words[i] at every position into a prefix and a suffix. If the prefix is a palindrome, then any word equal to the reverse of the suffix can sit before words[i] to form a palindrome. If the suffix is a palindrome, then any word equal to the reverse of the prefix can sit after words[i]. Storing every word with its index in a hash map turns each of these lookups into O(1), so the only cost per word is forming the prefixes and suffixes and reversing them.
Suppose words[i] + words[j] is a palindrome and the two words have different lengths (equal-length pairs are the exact-reverse case, covered by the empty split). Say words[i] is the longer one. Matching characters from both ends, words[j] fully cancels against the matching tail of words[i], leaving a contiguous block in the middle of words[i] that is unmatched. Because the whole string is a palindrome, that middle block must read the same forwards and backwards, so it is a palindrome on its own. Splitting words[i] at the point where words[j] stops covering it reproduces exactly this structure: one side is the palindromic middle, the other side reverses to words[j]. Trying every split point therefore finds every pair.
i, try every split position j from 0 to len(word) (inclusive, so both the empty prefix and the empty suffix are covered):prefix = word[0..j] and suffix = word[j..end].prefix is a palindrome and the reverse of suffix exists in the map at some index k != i, then words[k] placed before the word completes a palindrome, so add [k, i].suffix is non-empty and a palindrome, and the reverse of prefix exists at some index k != i, then words[k] placed after the word completes a palindrome, so add [i, k].The suffix non-empty guard in Check 2 prevents counting the same pair twice. When j equals len(word), the suffix is empty and the prefix is the whole word. That case is already handled by Check 1 at j = 0, where the prefix is empty (a palindrome) and the suffix is the whole word, so skipping the empty-suffix branch removes the duplicate.
Loading animation...
The hash map approach avoids checking all pairs, but it still builds and reverses O(k) substrings per word. The next approach stores the words in a trie of their reversed forms and walks it character by character, matching partners during the traversal instead of constructing substrings.
Build a trie from the reversed words, then for each word w walk the trie character by character following w. Two situations produce a pair, both finding a partner p such that w + p is a palindrome:
w remain. That stored word equals the reverse of a prefix of w, so it cancels that prefix. The remaining suffix of w must itself be a palindrome for the concatenation to work, so we check that before recording the pair.w and land on some node. Any reversed word stored at or below that node has w as a prefix of its reverse, so it covers all of w and extends further. The extra characters it carries must form a palindrome. We pre-compute, at each node during insertion, the list of word indices whose remaining (deeper) portion is a palindrome, so the lookup at the end is a single list read.i (counting from the end), the still-unconsumed front of the word is word[0..i]. If that front is a palindrome, record this word's index in a palindromeBelow list at the current node. At the final node, store the word's index as wordIdx and also append it to palindromeBelow (the empty remainder counts as a palindrome).wordIdx set and not equal to the current word's index) and the unread tail of the current word is a palindrome, record [idx, wordIdx]. After consuming the whole word, every index in the current node's palindromeBelow list (except the word itself) forms a pair [idx, k].Loading animation...