近日參加了一家國內某知名互聯網公司的在線筆試,有一道算法題比較有意思,拿出來分享一下。算法
給定一個字符串,能夠隨意刪除字符串裏的任何字符(也能夠不刪除),目標是刪除一些字符後讓剩下的字符可以組成迴文,並返回該輸入字符串所能構成的最長迴文長度。數組
例如,輸入:"eabbfa"。spa
刪除e和f後,構成"abba",所以返回4。code
先看一個遞歸的方法:blog
int recursive(string& s, int start, int end) { if (start == end) { return 1; } if (start > end) { return 0; } if (s[start] == s[end]) { return recursive(s, start + 1, end - 1) + 2; } else { return max(recursive(s, start, end - 1), recursive(s, start + 1, end)); } } int longestPalindrom(string s) { return recursive(s, 0, s.length() - 1); }
可是遞歸方法會計算許多重複子問題,致使效率降低。遞歸
這題能夠用動態規劃來作。申請一個二維數組,dp[i][j],表明了字符串從 i 到 j 位置的子串中可以包含的最長迴文長度。字符串
顯然,這裏的起始條件是dp[i][i] = 1,i取值爲[0, s.length() - 1],由於只有一個字符的字符串必定是迴文。string
以後能夠用 k 表明 i 和 j 的距離,從1開始遞增。io
當s[i] == s[j]時,進行狀態轉移,返回dp[i][j] = max(dp[i + 1][j - 1] + 2, max(dp[i + 1][j] + 1, dp[i][j - 1] + 1));class
大體思路就是這樣。
int longestPalindrom(string s) { int len = s.length(); if (len <= 1) { return len; } vector<vector<int>> dp(len, vector<int>(len, 0)); int max_len = 1; // single character is palindrom for (int i = 0; i < len; ++i) { dp[i][i] = 1; } // k is distance between i and j for (int k = 1; k < len; ++k) { for (int i = 0; i < len - k; ++i) { int j = i + k; if (s[i] == s[j]) { dp[i][j] = max(dp[i + 1][j - 1] + 2, max(dp[i + 1][j] + 1, dp[i][j - 1] + 1)); } else { dp[i][j] = max(dp[i + 1][j - 1], max(dp[i + 1][j], dp[i][j - 1])); } } } return dp[0][len-1]; } int main() { string s = "rdgsaraytbbdrcdfcbsbaaoio"; int res = longestPalindrom(s); return 0; }
若是有更好的思路或者發現代碼有紕漏,歡迎留言指正。