高级农民
- 积分
- 2593
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2020-1-21
- 最后登录
- 1970-1-1
|
本帖最后由 Falldawn 于 2021-4-15 23:57 编辑
https://leetcode.com/problems/palindrome-pairs/
參考:https://leetcode.com/problems/palindrome-pairs/solution/
这题参考答案的说明讲得非常详细,不过我更喜欢讨论区的答案https://leetcode.com/problems/pa ... stand-JAVA-Solution,不知道我的空间复杂度分析得对不对,这里假设每一个new出来的string 的空间都为O(1)
Solution 1: HashMap
There are several cases to be considered that isPalindrome(s1 + s2):
Case1: If s1 is a blank string, then for any string that is palindrome s2, s1+s2 and s2+s1 are palindrome.
Case 2: If s2 is the reversing string of s1, then s1+s2 and s2+s1 are palindrome.
Case 3: If s1[0:cut] is palindrome and there exists s2 is the reversing string of s1[cut+1:] , then s2+s1 is palindrome.
Case 4: Similar to case3. If s1[cut+1: ] is palindrome and there exists s2 is the reversing string of s1[0:cut] , then s1+s2 is palindrome.
To make the search faster, build a HashMap to store the String-idx pairs.
Let n be the number of words, and k be the average length of the word.
Time : O(n * k^2).
Space: O(n) for the HashMap. For each word, we're making a list of all possible pair words which is O(n * k), so O(n * k).
- public List<List<Integer>> palindromePairs(String[] words) {
- List<List<Integer>> res = new ArrayList<List<Integer>>();
- if(words == null || words.length == 0){
- return res;
- }
- //build the map save the key-val pairs: String - idx
- Map<String, Integer> map = new HashMap<>();
- for(int i = 0; i < words.length; i++){
- map.put(words[i], i);[/i]
- [i] }[/i]
- //special cases: "" can be combine with any palindrome string
- Integer blankIdx = map.get("");
- if(blankIdx != null){
- for(int i = 0; i < words.length; i++){
- if(i != blankIdx && isPalindrome(words, 0, words.length() - 1)){
- res.add(Arrays.asList(blankIdx, i));
- res.add(Arrays.asList(i, blankIdx));
- }
- }
- }
- //find all string and reverse string pairs
- for(int i = 0; i < words.length; i++){
- String reversed = new StringBuilder(words).reverse().toString();
- Integer reversedIdx = map.get(reversed);
- if (reversedIdx != null && reversedIdx != i) {
- res.add(Arrays.asList(i, reversedIdx));
- }
- }
- //find the pair s1, s2 that
- //case1 : s1[0:cut] is palindrome and s1[cut+1:] = reverse(s2) => (s2, s1)
- //case2 : s1[cut+1:] is palindrome and s1[0:cut] = reverse(s2) => (s1, s2)
- for(int i = 0; i < words.length; i++){
- String cur = words;
- for(int cut = 1; cut < cur.length(); cut++){
- if(isPalindrome(cur, 0, cut - 1)){
- String right = cur.substring(cut);
- String reversedRight = new StringBuilder(right).reverse().toString();
- Integer found = map.get(reversedRight);
- if(found != null && found != i) {
- res.add(Arrays.asList(found, i));
- }
- }
- if(isPalindrome(cur, cut, cur.length() - 1)){
- String left = cur.substring(0, cut);
- String reversedLeft = new StringBuilder(left).reverse().toString();
- Integer found = map.get(reversedLeft);
- if(found != null && found != i) {
- res.add(Arrays.asList(i, found));
- }
- }
- }
- }
- return res;
- }
- public boolean isPalindrome(String s, int i, int j){
- while(i < j){
- if(s.charAt(i) != s.charAt(j)){
- return false;
- }
- i++;
- j--;
- }
- return true;
- }
复制代码
Solution 2: HashMap with one pass
If we look closely at case 1 and case 2, they are actually the special cases of case 3 and case 4 and we can merge them together with just one pass.
Time : O(n * k^2).
Space: O(n) for the HashMap. For each word, we're making a list of all possible pair words which is O(n * k), so O(n * k).
- public List<List<Integer>> palindromePairs(String[] words) {
- List<List<Integer>> res = new ArrayList<>();
- if(words == null || words.length <= 1){
- return res;
- }
- //build the map save the key-val pairs: String - idx
- Map<String, Integer> map = new HashMap<>();
- for(int i = 0; i < words.length; i++){
- map.put(words, i);
- }
- //find the pair s1, s2 that
- //case1 : s1[0:cut] is palindrome and s1[cut+1:] = reverse(s2) => (s2, s1)
- //case2 : s1[cut+1:] is palindrome and s1[0:cut] = reverse(s2) => (s1, s2)
- for(int i = 0; i < words.length; i++){
- String cur = words;
- for(int cut = 0; cut < cur.length(); cut++){
- if(isPalindrome(cur, 0, cut - 1)){
- String right = cur.substring(cut);
- String reversedRight = new StringBuilder(right).reverse().toString();
- Integer found = map.get(reversedRight);
- if(found != null && found != i) {
- res.add(Arrays.asList(found, i));
- if (right.isEmpty()) {// handle "" in words list
- res.add(Arrays.asList(i, found));
- }
- }
- }
- if(isPalindrome(cur, cut, cur.length() - 1)){
- String left = cur.substring(0, cut);
- String reversedLeft = new StringBuilder(left).reverse().toString();
- Integer found = map.get(reversedLeft);
- if(found != null && found != i) {
- res.add(Arrays.asList(i, found));
- if (left.isEmpty()) {// handle "" in words list
- res.add(Arrays.asList(found, i));
- }
- }
- }
- }
- }
- return res;
- }
- public boolean isPalindrome(String s, int i, int j){
- while(i < j){
- if(s.charAt(i) != s.charAt(j)){
- return false;
- }
- i++;
- j--;
- }
- return true;
- }
复制代码
参考答案对Trie解法的讲解也非常详细,不过讨论区的大牛分析也非常好https://leetcode.com/problems/pa ... with-Trie-structure
Both building and searching the Trie structure take O(n * k^2), which sets the total time complexity of the solution.
The Trie is the main space usage. In the worst case, each of the O(n⋅k) letters in the input would be on separate nodes, and each node would have up to n indexes in its list. This gives us a worst case of O(n2⋅k), which is strictly larger than the input or the output.
Here is the complete Java program:
-
- class TrieNode {
- TrieNode[] next;
- int index;
- List<Integer> list;
- TrieNode() {
- next = new TrieNode[26]; // record next letter in a word, a is 0, b is 1, c is 2 ..., z is 25
- index = -1; // record if this trieNode is a word, means from root to this trienode can form a word in words array and the index in words array. if no word ends on this node, index is -1.
- list = new ArrayList<>(); // if from this letter in a word to beginning of this word can form a palindrome, add the word index in the words array.
- }
- }
- 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);
- }
- for (int i = 0; i < words.length; i++) {
- searchPalindrome(words, res, root, i);
- }
- return res;
- }
- private void addWord(TrieNode node, String word, int indexInWords) {
- for (int i = word.length() - 1; i >= 0; i--) {
- int trieIndex = word.charAt(i) - 'a';
- if (node.next[trieIndex] == null) {
- node.next[trieIndex] = new TrieNode();
- }
- // if from this trienode to the beginning of a word is palindrome.
- if (isPalindrome(word, 0, i)) {
- node.list.add(indexInWords);
- }
- node = node.next[trieIndex];
- }
- node.index = indexInWords;// end of word, set word index
- }
- private void searchPalindrome(String word, List<List<Integer>> res, TrieNode node, int indexInWords) {
- // search part 1: compare the word to trie (the word may longer than the counterparty in trie)
- //case1 : s1[j:] is palindrome and s1[0:j) = reverse(s2) => (s1, s2)
- for (int i = 0; i < word.length(); i++) {
- if (node.index != -1 && node.index != indexInWords && isPalindrome(word, i, word.length() - 1)) {
- res.add(Arrays.asList(indexInWords, node.index));
- }
- node = node.next[word.charAt(i) - 'a'];
- if (node == null) {
- return;
- }
- }
- // search part 2: the word is end, only check the rest in trie. (the counterparty in trie may longer than the word )
- // if it is the last trienode of a word, add to result.
- // Two words are the same length, form a palindrome
- if(node.index != -1 && indexInWords != node.index){
- res.add(Arrays.asList(indexInWords, node.index));
- }
- // if from this trienode to the beginning of a word can form a palindrome, add it to the result.
- //case2 : s2[0:cut] is palindrome and s1[] = reverse(s2[cut:]) => (s1, s2)
- for (int k : node.list) {
- if (indexInWords != k) {
- res.add(Arrays.asList(indexInWords, k));
- }
- }
- }
- private boolean isPalindrome(String word, int i, int j) {
- while (i < j) {
- if (word.charAt(i) != word.charAt(j)) {
- return false;
- }
- i++;
- j--;
- }
- return true;
- }
复制代码
参考答案最后还对Online Algorithms进行了说明 |
|