Welcome to Subscribe On Youtube

Question

Formatted question description: https://leetcode.ca/all/336.html

You are given a 0-indexed array of unique strings words.

A palindrome pair is a pair of integers (i, j) such that:

  • 0 <= i, j < words.length,
  • i != j, and
  • words[i] + words[j] (the concatenation of the two strings) is a palindrome.

Return an array of all the palindrome pairs of words.

 

Example 1:

Input: words = ["abcd","dcba","lls","s","sssll"]
Output: [[0,1],[1,0],[3,2],[2,4]]
Explanation: The palindromes are ["abcddcba","dcbaabcd","slls","llssssll"]

Example 2:

Input: words = ["bat","tab","cat"]
Output: [[0,1],[1,0]]
Explanation: The palindromes are ["battab","tabbat"]

Example 3:

Input: words = ["a",""]
Output: [[0,1],[1,0]]
Explanation: The palindromes are ["a","a"]

 

Constraints:

  • 1 <= words.length <= 5000
  • 0 <= words[i].length <= 300
  • words[i] consists of lowercase English letters.

Algorithm

step1: put the reverse order of the words into a map

step 2 & 3: check the prefix of each word.

It can form a palindrome iff

  • prefix in the map AND
  • rest substring is a palindrome.

e.g. “abll”, if the prefix is ab, and the map contains a “ab”, then we can form a palindrome which is abllba

Check the postfix of each word. The word can form a palindrome iff

  • postfix is in the map AND
  • the rest of prefix is a palindrome e.g. “llab”, where the postfix is ab, if the map cotnains a “ab”, then we can form a palindrome of ballab

Code

  • import java.util.*;
    
    public class Palindrome_Pairs {
    
        public class Solution {
    
            public List<List<Integer>> palindromePairs(String[] words) {
                List<List<Integer>> result = new ArrayList<>();
    
                if (words == null || words.length == 0) {
                    return result;
                }
    
                // step1: put the reverse order of the words into a map
                Map<String, Integer> map = new HashMap<>();
                for (int i = 0; i < words.length; i++) {
                    String reversedWord = reverse(words[i]);
                    map.put(reversedWord, i);
                }
    
                // step 2 & 3: check the prefix of each word.
                // It can form a palindrome iff
                //      prefix in the map AND
                //      rest substring is a palindrome.
                // e.g. "abll", if the prefix is ab, and the map contains a "ab", then
                //      we can form a palindrome which is abllba
    
                // check the postfix of each word.
                // The word can form a palindrome iff
                //      postfix is in the map AND
                //      the rest of prefix is a palindrome
                // e.g. "llab", where the postfix is ab, if the map cotnains a "ab", then
                //      we can form a palindrome of ballab
                for (int i = 0; i < words.length; i++) {
                    String word = words[i];
    
                    // get prefix of each word
                    for (int j = 0; j <= word.length(); j++) {
                        String prefix = word.substring(0, j);
                        String rest = word.substring(j);
                        // !=i, not the same word itself
                        if (map.containsKey(prefix) && isPalindrome(rest) && map.get(prefix) != i) {
                            List<Integer> curr = new ArrayList<>();
                            curr.add(i);
                            curr.add(map.get(prefix));
                            result.add(curr);
                        }
                    }
    
                    // get postfix of each word
                    // substring to is exclusive
                    for (int j = word.length(); j > 0; j--) {
                        String postfix = word.substring(j);
                        String rest = word.substring(0, j);
                        if (map.containsKey(postfix) && isPalindrome(rest) &&
                            map.get(postfix) != i) {
                            List<Integer> curr = new ArrayList<>();
                            curr.add(map.get(postfix));
                            curr.add(i);
                            result.add(curr);
                        }
                    }
                }
    
                return result;
            }
    
            private String reverse(String s) {
                if (s == null || s.length() == 0) {
                    return "";
                }
    
                char[] array = s.toCharArray();
                int start = 0;
                int end = array.length - 1;
    
                while (start < end) {
                    swap(start, end, array);
                    start++;
                    end--;
                }
    
                return new String(array);
            }
    
            private void swap(int start, int end, char[] array) {
                char temp = array[start];
                array[start] = array[end];
                array[end] = temp;
            }
    
            private boolean isPalindrome(String s) {
                if (s == null || s.length() == 0) {
                    return true;
                }
    
                int start = 0;
                int end = s.length() - 1;
    
                while (start < end) {
                    if (s.charAt(start) != s.charAt(end)) {
                        return false;
                    }
                    start++;
                    end--;
                }
    
                return true;
            }
        }
    
        // O(n * k^2)
        // @todo
        class Solution_Trie {
            public List<List<Integer>> palindromePairs(String[] words) {
                List<List<Integer>> res = new ArrayList<>();
    
                TrieNode root = new TrieNode();
    
                for (int i = 0; i < words.length; i++) {
                    addWord(root, words[i], i);
                }
    
                for (int i = 0; i < words.length; i++) {
                    search(words, i, root, res);
                }
    
                return res;
            }
    
            private void addWord(TrieNode root, String word, int index) {
                for (int i = word.length() - 1; i >= 0; i--) {
                    int j = word.charAt(i) - 'a';
    
                    if (root.next[j] == null) {
                        root.next[j] = new TrieNode();
                    }
    
                    if (isPalindrome(word, 0, i)) {
                        root.list.add(index);
                    }
    
                    root = root.next[j];
                }
    
                root.list.add(index);
                root.index = index;
            }
    
            private void search(String[] words, int i, TrieNode root, List<List<Integer>> res) {
                for (int j = 0; j < words[i].length(); j++) {
                    if (root.index >= 0 && root.index != i && isPalindrome(words[i], j, words[i].length() - 1)) {
                        res.add(Arrays.asList(i, root.index));
                    }
    
                    root = root.next[words[i].charAt(j) - 'a'];
                    if (root == null) return;
                }
    
                for (int j : root.list) {
                    if (i == j) continue;
                    res.add(Arrays.asList(i, j));
                }
            }
    
            private boolean isPalindrome(String word, int i, int j) {
                while (i < j) {
                    if (word.charAt(i++) != word.charAt(j--)) return false;
                }
    
                return true;
            }
    
        }
    
        class TrieNode {
            TrieNode[] next;
            int index;
            List<Integer> list;
    
            TrieNode() {
                next = new TrieNode[26];
                index = -1;
                list = new ArrayList<>();
            }
        }
    
    }
    
    ############
    
    class Solution {
        private static final int BASE = 131;
        private static final long[] MUL = new long[310];
        private static final int MOD = (int) 1e9 + 7;
        static {
            MUL[0] = 1;
            for (int i = 1; i < MUL.length; ++i) {
                MUL[i] = (MUL[i - 1] * BASE) % MOD;
            }
        }
        public List<List<Integer>> palindromePairs(String[] words) {
            int n = words.length;
            long[] prefix = new long[n];
            long[] suffix = new long[n];
            for (int i = 0; i < n; ++i) {
                String word = words[i];
                int m = word.length();
                for (int j = 0; j < m; ++j) {
                    int t = word.charAt(j) - 'a' + 1;
                    int s = word.charAt(m - j - 1) - 'a' + 1;
                    prefix[i] = (prefix[i] * BASE) % MOD + t;
                    suffix[i] = (suffix[i] * BASE) % MOD + s;
                }
            }
            List<List<Integer>> ans = new ArrayList<>();
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    if (check(i, j, words[j].length(), words[i].length(), prefix, suffix)) {
                        ans.add(Arrays.asList(i, j));
                    }
                    if (check(j, i, words[i].length(), words[j].length(), prefix, suffix)) {
                        ans.add(Arrays.asList(j, i));
                    }
                }
            }
            return ans;
        }
    
        private boolean check(int i, int j, int n, int m, long[] prefix, long[] suffix) {
            long t = ((prefix[i] * MUL[n]) % MOD + prefix[j]) % MOD;
            long s = ((suffix[j] * MUL[m]) % MOD + suffix[i]) % MOD;
            return t == s;
        }
    }
    
  • // OJ: https://leetcode.com/problems/palindrome-pairs/
    // Time: O(N * L^2)
    // Space: O(NL)
    // Ref: https://leetcode.com/problems/palindrome-pairs/discuss/79195/O(n-*-k2)-java-solution-with-Trie-structure
    struct TrieNode {
        TrieNode *next[26] = {};
        int index = -1;
        vector<int> palindromeIndexes;
    };
    class Solution {
        TrieNode root; // Suffix trie
        void add(string &s, int i) {
            auto node = &root;
            for (int j = s.size() - 1; j >= 0; --j) {
                if (isPalindrome(s, 0, j)) node->palindromeIndexes.push_back(i); // A[i]'s prefix forms a palindrome
                int c = s[j] - 'a';
                if (!node->next[c]) node->next[c] = new TrieNode();
                node = node->next[c];
            }
            node->index = i;
            node->palindromeIndexes.push_back(i); // A[i]'s prefix is empty string here, which is a palindrome.
        }
        bool isPalindrome(string &s, int i, int j) {
            while (i < j && s[i] == s[j]) ++i, --j;
            return i >= j;
        }
    public:
        vector<vector<int>> palindromePairs(vector<string>& A) {
            int N = A.size();
            for (int i = 0; i < N; ++i) add(A[i], i);
            vector<vector<int>> ans;
            for (int i = 0; i < N; ++i) {
                auto s = A[i];
                auto node = &root;
                for (int j = 0; j < s.size() && node; ++j) {
                    if (node->index != -1 && node->index != i && isPalindrome(s, j, s.size() - 1)) ans.push_back({ i, node->index }); // A[i]'s prefix matches this word and A[i]'s suffix forms a palindrome
                    node = node->next[s[j] - 'a'];
                }
                if (!node) continue;
                for (int j : node->palindromeIndexes) { // A[i] is exhausted in the matching above. If a word whose prefix is palindrome after matching its suffix with A[i], then this is also a valid pair
                    if (i != j) ans.push_back({ i, j });
                }
            }
            return ans;
        }
    };
    
  • class Solution:
        def palindromePairs(self, words: List[str]) -> List[List[int]]:
            d = {w: i for i, w in enumerate(words)}
            ans = []
            for i, w in enumerate(words):
                for j in range(len(w) + 1):
                    a, b = w[:j], w[j:]
                    ra, rb = a[::-1], b[::-1]
                    if ra in d and d[ra] != i and b == rb:
                        ans.append([i, d[ra]])
                    if j and rb in d and d[rb] != i and a == ra:
                        ans.append([d[rb], i])
            return ans
    
    ############
    
    class Solution(object):
      def palindromePairs(self, words):
        """
        :type words: List[str]
        :rtype: List[List[int]]
        """
        ans = []
        d = {}
        for i, word in enumerate(words):
          d[word] = i
    
        for i, word in enumerate(words):
          if word == "":
            ans.extend([(i, j) for j in range(len(words)) if i != j and words[j] == words[j][::-1]])
            continue
          for j in range(len(word)):
            left = word[:j]
            right = word[j:]
            if right == right[::-1] and left[::-1] in d and d[left[::-1]] != i:
              ans.append((i, d[left[::-1]]))
            if left == left[::-1] and right[::-1] in d and d[right[::-1]] != i:
              ans.append((d[right[::-1]], i))
        return ans
    
    
  • func palindromePairs(words []string) [][]int {
    	base := 131
    	mod := int(1e9) + 7
    	mul := make([]int, 310)
    	mul[0] = 1
    	for i := 1; i < len(mul); i++ {
    		mul[i] = (mul[i-1] * base) % mod
    	}
    	n := len(words)
    	prefix := make([]int, n)
    	suffix := make([]int, n)
    	for i, word := range words {
    		m := len(word)
    		for j, c := range word {
    			t := int(c-'a') + 1
    			s := int(word[m-j-1]-'a') + 1
    			prefix[i] = (prefix[i]*base)%mod + t
    			suffix[i] = (suffix[i]*base)%mod + s
    		}
    	}
    	check := func(i, j, n, m int) bool {
    		t := ((prefix[i]*mul[n])%mod + prefix[j]) % mod
    		s := ((suffix[j]*mul[m])%mod + suffix[i]) % mod
    		return t == s
    	}
    	var ans [][]int
    	for i := 0; i < n; i++ {
    		for j := i + 1; j < n; j++ {
    			if check(i, j, len(words[j]), len(words[i])) {
    				ans = append(ans, []int{i, j})
    			}
    			if check(j, i, len(words[i]), len(words[j])) {
    				ans = append(ans, []int{j, i})
    			}
    		}
    	}
    	return ans
    }
    
  • using System.Collections.Generic;
    using System.Linq;
    
    public class Solution {
        public IList<IList<int>> PalindromePairs(string[] words) {
            var results = new List<IList<int>>();
            var reverseDict = words.Select((w, i) => new {Word = w, Index = i}).ToDictionary(w => new string(w.Word.Reverse().ToArray()), w => w.Index);
    
            for (var i = 0; i < words.Length; ++i)
            {
                var word = words[i];
                for (var j = 0; j <= word.Length; ++j)
                {
                    if (j > 0 && IsPalindrome(word, 0, j - 1))
                    {
                        var suffix = word.Substring(j);
                        int pairIndex;
                        if (reverseDict.TryGetValue(suffix, out pairIndex) && i != pairIndex)
                        {
                            results.Add(new [] { pairIndex, i});
                        }
                    }
                    if (IsPalindrome(word, j, word.Length - 1))
                    {
                        var prefix = word.Substring(0, j);
                        int pairIndex;
                        if (reverseDict.TryGetValue(prefix, out pairIndex) && i != pairIndex)
                        {
                            results.Add(new [] { i, pairIndex});
                        }
                    }
                }
            }
    
            return results;
        }
    
        private bool IsPalindrome(string word, int startIndex, int endIndex)
        {
            var i = startIndex;
            var j = endIndex;
            while (i < j)
            {
                if (word[i] != word[j]) return false;
                ++i;
                --j;
            }
            return true;
        }
    }
    

All Problems

All Solutions