[LeetCode] 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"
.
Credits:
Special thanks to
這道題讓我們求最短的迴文串,LeetCode中關於迴文串的其他的題目有 Palindrome Number 驗證迴文數字, Validate Palindrome 驗證迴文字串, Palindrome Partitioning 拆分迴文串,Palindrome Partitioning II 拆分迴文串之二和 Longest Palindromic Substring 最長迴文串
C++ 解法一:
class Solution { public: string shortestPalindrome(string s) { string r = s; reverse(r.begin(), r.end()); string t = s + "#" + r; vector<int> next(t.size(), 0); for (int i = 1; i < t.size(); ++i) { int j = next[i - 1]; while (j > 0 && t[i] != t[j]) j = next[j - 1]; next[i] = (j += t[i] == t[j]); } return r.substr(0, s.size() - next.back()) + s; } };
Java 解法一:
public class Solution { public String shortestPalindrome(String s) { String r = new StringBuilder(s).reverse().toString(); String t = s + "#" + r; int[] next = new int[t.length()]; for (int i = 1; i < t.length(); ++i) { int j = next[i - 1]; while (j > 0 && t.charAt(i) != t.charAt(j)) j = next[j - 1]; j += (t.charAt(i) == t.charAt(j)) ? 1 : 0; next[i] = j; } return r.substring(0, s.length() - next[t.length() - 1]) + s; } }
從上面的Java和C++的程式碼中,我們可以看出來C++和Java在使用雙等號上的明顯的不同,感覺Java對於雙等號對使用更加苛刻一些,比如Java中的雙等號只對primitive類資料結構(比如int, char等)有效,但是即便有效,也不能將結果直接當1或者0來用。而對於一些從Object派生出來的類,比如Integer或者String等,不能直接用雙等號來比較,而是要用其自帶的equals()函式來比較,因為雙等號判斷的是不是同一個物件,而不是他們所表示的值是否相同。同樣需要注意的是,Stack的peek()函式取出的也是物件,不能直接和另一個Stack的peek()取出的物件直接雙等,而是使用equals或者先將其中一個強行轉換成primitive類,再和另一個強行比較。
下面這種方法的寫法比較簡潔,雖然不是明顯的KMP演算法,但是也有其的影子在裡面,首先我們還是先將其的翻轉字串搞出來,然後比較原字串s的字首後翻轉字串t的對應位置的字尾是否相等,起始位置是比較s和t是否相等,如果相等,說明s本身就是迴文串,不用新增任何字元,直接返回即可;如果不想等,s去掉最後一位,t去掉第一位,繼續比較,以此類推直至有相等,或者迴圈結束,這樣我們就能將兩個字串在正確的位置拼接起來了。很有意思的是,這種方法對應Java寫法卻會TLE,無法通過OJ。
C++ 解法二:
class Solution { public: string shortestPalindrome(string s) { string t = s; reverse(t.begin(), t.end()); int n = s.size(), i = 0; for (i = n; i >= 0; --i) { if (s.substr(0, i) == t.substr(n - i)) { break; } } return t.substr(0, n - i) + s; } };
下面這種Java寫法也是在找相同的字首字尾,但是並沒有每次把字首字尾取出來比較,而是用兩個指標分別指向對應的位置比較,然後用end指向相同字尾的起始位置,最後再根據end的值來拼接兩個字串。有意思的是這種方法對應的C++寫法會TLE,跟上面正好相反,那麼我們是否能得出Java的substring操作略慢,而C++的reverse略慢呢,我也僅僅是猜測而已。
Java 解法三:
public class Solution { public String shortestPalindrome(String s) { int i = 0, end = s.length() - 1, j = end; char arr = s.toCharArray(); while (i < j) { if (arr[i] == arr[j]) { ++i; --j; } else { i = 0; --end; j = end; } } return new StringBuilder(s.substring(end + 1)).reverse().toString() + s; } }
參考資料: