【leetcode】Shortest Palindrome(hard)★
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"
.
思路:
写了三份代码,各种超时。知道肯定要用之前学了几遍也记不住的方法了--判断最长回文的O(N)算法Manacher算法。
特别注意:
s[i]这样的写法非常耗时!
//Manacher算法 string shortestPalindrome(string s){ if(s.size() <= 1) return s; string ss = "$#"; for(auto c : s) //这样写只需要12ms ss+=c, ss+='#'; //for(int i = 0; i < s.size(); ++i) //先把字符串转换一下 这样写非常慢 240ms时间都耗在这里了 // ss = ss + s[i] + "#"; vector<int> P(2 * s.size() + 2, 0); //存储以i为中心回文的最大半径 int e = 0; int id = 1, mx = 0; //id为可以管的最远的回文的中心点 mx是id可以管到的最远距离 在回文的后面一个位置 for(int i = 1; i < ss.size(); ++i) { P[i] = (mx > i) ? min(P[2 * id - i], mx - i) : 1; //根据之前的信息 获取当前中心点的最短回文长度 while(i + P[i] < ss.size() && ss[i + P[i]] == ss[i - P[i]]) //扩展回文长度 注意不要越界 P[i]++; if(i + P[i] > mx) //更新最远距离和中心点 { mx = i + P[i]; id = i; } if(i == P[i]) e = i + P[i] - 1; //该回文的第一个位置是源字符串的第一个字母 记录回文截至位置 } e = e / 2 - 1; //把回文最长的截至位置转换为在原字符串中的位置 string rs = s.substr(e + 1, s.size() - e); //在字符串前面补上不够回文的部分 reverse(rs.begin(), rs.end()); return rs + s; }
大神4ms的代码,其实思路都一样,可大神的代码时间就是短!
string shortestPalindrome(string s) { int n1=s.length(); string mystr="$";//2*n1+1 int n=2*n1+1; for(auto c : s) mystr+=c, mystr+='$'; int* plen=new int[n]; int pali_len=1; int i, k, mid=0, l=-1; for(i=0; i<=n/2; i++) { k=0; if(i<l&&2*mid-i>-1) k=min(plen[2*mid-i], l-i); while(i-k-1>-1&&i+k+1<n&&mystr[i-k-1]==mystr[i+k+1]) k++; plen[i]=k; if(i+k>l) mid=i, l=i+k; } for(i=n/2; i>-1; i--) if(plen[i]==i) {pali_len=i; break;} string t=s.substr(i); reverse(t.begin(), t.end()); return t+s; }
三个超时的常规思路代码:
bool isPalindrome(string s, int start, int end) { while(start <= end) if(s[start++] != s[end--]) return false; return true; } //超时 string shortestPalindrome1(string s) { if(s.size() <= 1) return s; int e = 0; for(int i = s.size() - 1; i >= 0; --i) //找到包括第一个字母的最长回文 { if(isPalindrome(s, 0, i)) { e = i; break; } } string rs = s.substr(e + 1, s.size() - e); reverse(rs.begin(), rs.end()); return rs + s; } //超时 string shortestPalindrome2(string s){ if(s.size() <= 1) return s; for(int i = s.size() - 1; i >= 0; --i) //遍历可能回文的最后一个位置 { if(s[i] == s[0]) //如果第一个字符和最后位置相同 { int slen = (i + 1) / 2 ; //回文一半的长度 奇数个则不要最中间的 string s1 = s.substr(0, slen); string s2 = s.substr(i - slen + 1, slen); reverse(s2.begin(), s2.end()); if(s1 == s2) //是回文 { string rs = s.substr(i + 1, s.size() - i); reverse(rs.begin(), rs.end()); return rs + s; } } } } //超时 string shortestPalindrome3(string s){ if(s.size() <= 1) return s; for(int i = s.size() - 1; i >= 0; --i) //遍历可能回文的最后一个位置 { if(s[i] == s[0]) //如果第一个字符和最后位置相同 { int slen = (i + 1) / 2 ; //回文一半的长度 奇数个则不要最中间的 string s1 = s.substr(0, slen); string s2 = s.substr(i - slen + 1, slen); reverse(s2.begin(), s2.end()); unordered_set<string> uset; uset.insert(s1); if(uset.find(s2) != uset.end()) { string rs = s.substr(i + 1, s.size() - i); reverse(rs.begin(), rs.end()); return rs + s; } } } }