5. Longest Palindromic Substring && 214. Shortest Palindrome && 336. Palindrome Pairs

5. Longest Palindromic Substring

Find the longest palindromic substring in string S. You may assume that there exists one unique longest palindromic substring.

class Solution {
  private int resultBegin, resultLen;

  public String longestPalindrome(String s) {
    int len = s.length();
    if (len < 2)
      return s;

    for (int center = 0; center < len - 1; ++center) {
      extendBothEnds(s, center, center);  //when longestPalindrome length is odd
      extendBothEnds(s, center, center + 1); //when longestPalindrome length is even
    }
    return s.substring(resultBegin, resultBegin + resultLen);
  }

  private void extendBothEnds(String s, int left, int right) {
    while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
      --left;
      ++right;
    }

    ++left;
    --right;
    int currentLen = right - left + 1;
    if (currentLen > resultLen) {
      resultBegin = left;
      resultLen = currentLen;
    }
  }
}

 

214. Shortest Palindrome 

Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.

For example:

Given "aacecaaa", return "aaacecaaa".

Given "abcd", return "dcbabcd". 

 

An intuitive solution, but got TLE.

The idea is to get the longest palindrome prefix, and then reverse the rest of the string and add it to the front. O(n2)  complexity.

class Solution {
  public String shortestPalindrome(String s) {
    if (s.length() <= 1)
      return s;
    int end = s.length();
    for (; end >= 1; --end)
      if (isPalindrome(s.substring(0, end)))
        break;
    return new StringBuilder(s.substring(end)).reverse().toString() + s;
  }

  private boolean isPalindrome(String s) {
    for (int left = 0, right = s.length() - 1; left < right; ++left, --right)
      if (s.charAt(left) != s.charAt(right))
        return false;
    return true;
  }
}

 

Recursion Solution

Got from: https://discuss.leetcode.com/topic/21068/my-7-lines-recursive-java-solution/4
class Solution {
  /**
   * The goal is to complete s into a Palindrome. We could tackle this problem little by little.
   * <p>
   * We need to split the string into two parts, s[0,j), s[j,len), such that
   * s[j, len) is impossible to be matched by s itself, we must prepend a reversed part for it
   * in front of s.
   * And we recursively do the same thing for s[0,j) to get this:
   * reverse(s[j,len)) + f(s[0,j]) + s[j,len)
   * <p>
   * One way to find j is that
   * s[j] is unique but not in the "middle" in the string.
   */
  public String shortestPalindrome(String s) {
    int j = 0;
    for (int i = s.length() - 1; i >= 0; --i) //iterate i all the way to 0, such that we try to use s itself to match itself.
      if (s.charAt(i) == s.charAt(j))  //if j could be matched by i, whather on j itself(mid point), j's right or j's left.
        ++j;
    if (j == s.length())
      return s;

    String suffix = s.substring(j);
    return new StringBuilder(suffix).reverse().toString() + shortestPalindrome(s.substring(0, j)) + suffix;
  }
}

 

 

Solution2: KMP algorithm. Knuth-Morris-Pratt Algorithm

KMP algorithm enables subString search in string S with O(len(subString)+len(S))
 

c a t a c b # b c a t a c

0 0 0 0 1 0 0 0 1 2 3 4 5

if we don't add #, "aaaaa" wouldn't work.

class Solution {
  public String shortestPalindrome(String s) {
     if(s.length() <= 1)
        return s;
    String temp = s + "#"+ new StringBuilder(s).reverse().toString();
    int[] kmp = getTable(temp);
    //get the maximum palin part in s starts from 0
    return new StringBuilder(s.substring(kmp[kmp.length - 1])).reverse().toString() + s;
  }

  /**
   * ml indicates the max MatchedLength for index i based on Knuth–Morris–Pratt algorithm
   * "cacfcaca" returns [0, 0, 1, 0, 1, 2, 3, 2]
   * "babbbabbaba" returns [0, 0, 1, 1, 1, 2, 3, 4, 2, 3, 2]
   */
  private static int[] getTable(String s) {
    int[] ml = new int[s.length()];
    for (int i = 1; i < s.length(); ++i) {
      /**
       * loc shows the location/index such that,
       * s.substring(loc-ml[loc-1],loc) of length ml[loc-1] has fully matched prefix,
       * we are trying to extend this substring by one more letter, which is at loc
       */
      int loc = i;
      while (loc >= 1) {
        //ml[loc - 1] gives how many prefix we have matched, so the next index to compare/match is ml[loc - 1]
        int nextToMatch = ml[loc - 1];
        if (s.charAt(i) == s.charAt(nextToMatch)) {
          ml[i] = nextToMatch + 1;
          break;
        }
        /**
         * If we don't find a match, then we try to re-use a shorter match
         * Think about "cacfcacb", if we cannot match 'f' with 'b', then we cannot use the fully matched "cac".
         * So, we go to previous letter 'c' at index 2, and check what's length that has been matched until 'c',
         * and reuse that shorter string. 'c' has a matched length 1, so we will compare 'a'(at 1) with 'b'...
         * Do this recursively.
         */
        loc = nextToMatch;
      }
    }
    return ml;
  }
}

 

336. Palindrome Pairs

Given a list of unique words. Find all pairs of distinct indices (i, j) in the given list, so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome.

Example 1:
Given words = ["bat", "tab", "cat"]
Return [[0, 1], [1, 0]]
The palindromes are ["battab", "tabbat"]

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

 
Idea is similar to shortest palindrome,  we need to create a trie that reflects the reverse of all words. We match head and tail first, the rest of string/substring should be a palindrome.
public class Solution {
  class TrieNode {
    TrieNode[] children = new TrieNode[26];
    int wordArrayIndex = -1; //keep the index for current string in the array

    //keeps the list of word indices in array where the substring(or prefix) is a palindrome
    List<Integer> list = new ArrayList<>();
  }

  public List<List<Integer>> palindromePairs(String[] words) {
    List<List<Integer>> results = new ArrayList<>();

    TrieNode root = new TrieNode();
    for (int i = 0; i < words.length; i++) {
      //reverse each word and add it to the trie.
      addWord(root, words[i], i);
    }

    for (int i = 0; i < words.length; i++) {
      search(words[i], i, root, results);
    }

    return results;
  }

  private void addWord(TrieNode parent, String word, int wordIndex) {
    for (int i = word.length() - 1; i >= 0; --i) {
      int pos = word.charAt(i) - 'a';
      if (parent.children[pos] == null)
        parent.children[pos] = new TrieNode();

      if (isPalindrome(word, 0, i))//keep those words where the prefix is palindrome
        parent.list.add(wordIndex);
      parent = parent.children[pos];
    }
    parent.list.add(wordIndex);
    parent.wordArrayIndex = wordIndex;
  }

  private void search(String word, int wordIndex, TrieNode node, List<List<Integer>> results) {
    for (int i = 0; i < word.length(); ++i) {
      //Case 1: word accounts for >= half of the palindrome
      if (node.wordArrayIndex >= 0 && node.wordArrayIndex != wordIndex  //find word matching prefix.
        &&
        isPalindrome(word, i, word.length() - 1) //rest of the word is palindrome
        )
        results.add(Arrays.asList(wordIndex, node.wordArrayIndex));

      node = node.children[word.charAt(i) - 'a'];
      if (node == null)
        return;
    }

    //Case 2: word accounts for < half of the palindrome
    //e.g. "abc", "XXXcba", as long as "XXX" part is a palindrome, then we are good.
    for (int j : node.list) {
      if (wordIndex == j)
        continue;
      results.add(Arrays.asList(wordIndex, j));
    }
  }

  private boolean isPalindrome(String word, int i, int j) {
    while (i < j) {
      if (word.charAt(i++) != word.charAt(j--))
        return false;
    }
    return true;
  }
}

 

 
 
 
 
 

转载于:https://www.cnblogs.com/neweracoding/p/5698442.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值