题目:最长回文子串(C++)
看到这个题第一反应是做过啊,然后就开始写,等写完一测。emmmmm,原来是最长回文子串不是最长回文子序列,但是写都写了,我就把代码稍微修改了一下让适应于该题目,代码如下:
static const auto io_speed_up = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }(); class Solution { public: string longestPalindrome(string s) { int n = s.length(); if (n == 1 || n == 0) return s; int max = 0; int begin = 0; vector<vector<int> > dp(n,vector<int>(n)); for (int j = 0; j < n; j++) { dp[j][j] = 1; for (int i = j - 1; i >= 0; i--) { if (s[i] != s[j]) continue; if (dp[i + 1][j - 1] == 0 && i != j - 1) continue; dp[i][j] = dp[i + 1][j - 1] + 2; if (max >= dp[i][j]) continue; max = dp[i][j]; begin = i; } } if (max == 0) return s.substr(begin, 1); else return s.substr(begin, max); } };
然后这个代码一看就很搓啊,完全不适应该题,猜测效率一定很低,等提交过后看,果不其然,只超过了13.74%的代码。只好删了重写。
第二次写的代码效率超过了32.9%代码如下:
static const auto io_speed_up = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }(); class Solution { public: string longestPalindrome(string s) { int n = s.length(); if (n == 1 || n == 0) return s; int max = 0; int diff = 0; int begin = 0; int end = 0; int maxBegin = 0; int maxEnd = 0; for (int i = 0; i < n; ++i) { for (int j = n - 1; j > 0; --j) { if (max > j - i) break; begin = i; end = j; while (s[begin] == s[end] && begin <= end) { ++begin; --end; } if (end < begin && begin - end < 3) { diff = j - i; if (max < diff) { max = diff; maxBegin = i; maxEnd = j; } } } if (max > n - i - 1) break; } return s.substr(maxBegin, max + 1); } };
好吧我感觉这个应该是我能想出来的最优解法了,之后我看了一下网站上的解法。其中
倒数第二个的解法是这样的
class Solution { public: string longestPalindrome(string s) { int len = 0; int left = 0; int right = 0; string result = ""; int i = 0; while(i < s.size()) { left = i; right = i; while (right + 1 < s.size() && s[right] == s[right + 1]) right++; i = right + 1; while (left >= 0 && right < s.size() && s[left] == s[right]) { left--; right++; } if (len < right - left - 1) { len = right - left - 1; result = s.substr(left + 1, len); } } return result; } };
厉害了我的哥,你没优化输入输出流,然后在求结果字符串时还多次分割影响效率,竟然能达到6ms执行速度排第二。
我看了这位大神的代码,发现主要思路是从一个中心下标开始向两边检测,然后这里他用了一个巧妙得方法来处理偶数长度字符串要从两个字符开始的情况,即,选定中心后,循环检测之后的字符是否与选定的字符相等如果相等,则+1。这样如果出现偶数长度的字符串,开始时left和right指向相邻的两个字符。我把这个大神代码加了一下速,然后丢到了leetcode上检测,修改后的代码如下:
static const auto io_speed_up = []() { std::ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }(); class Solution { public: string longestPalindrome(string s) { int len = 0; int left = 0; int right = 0; int maxRes = 0; int leftRes = 0; int i = 0; while(i < s.size()) { left = i; right = i; while (right + 1 < s.size() && s[right] == s[right + 1]) right++; i = right + 1; while (left >= 0 && right < s.size() && s[left] == s[right]) { left--; right++; } if (len < right - left - 1) { len = right - left - 1; leftRes = left; maxRes = len; } } return s.substr(leftRes + 1, maxRes); } };
然后检测结果显示如下:
超过了100%并且显示执行速度为0ms,厉害了。
之后大致看了看之前排第一的算法,发现是用“#”把字符填充为原来的2n+1再从中心字符进行演算,完全没这位哥的巧妙,这里就不提了。