您的位置:首页 > 大数据 > 人工智能

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

2016-07-23 12:54 603 查看

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

https://www.youtube.com/watch?v=GTJr8OvyEVQ
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"]


Hash Table String Trie

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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: