Leetcode-214
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:输入: s = "aacecaaa" 输出: "aaacecaaa"
示例 2:输入: s = "abcd" 输出: "dcbabcd"
提示:
0 <= s.length <= 5 * 104s仅由小写英文字母组成
看到回文串第一反应是马拉车或者dp。
但是认真思考样例 ——
在示例一中,我们添加的前缀是"a",恰好是"aacecaa"这个最长回文前缀去掉之后的部分再进行一个反转。 示例二同理
仔细一想确乎是这样 ——
- 把整个原串反转后拼接,一定是回文串
- 如果前缀是回文的,那么不需要考虑
所以把原问题转化成“找 s 的最长回文前缀”
那么这又想到马拉车了 —— 跑马拉车的时候维护一下可以触碰到最左侧的回文串的长度,找到最长的即可
于是可以有第一种解法。
但是又看到了找”前缀“这件事情,所以不妨看看KMP能不能解 ——
先明确KMP维护的pi[i]表示P[0...i]中最长的相同的前后缀,所以就要寻找最长回文前缀的问题化归成最长相同前后缀问题 ——
“把回文串沿着中心劈开,把前一半反转一下就和后一半一致”
想到构造 ——
combine = s + "#" + reverse(s)直接看例子
a a c e c a a a # a a a c e c a a
^-----------^ 前缀来自 s
^-------------^ 后缀来自 reverse(s)a a c e c a a 就是我们需要找的最长回文前缀,同时也是KMP可以找到的相同前后缀
问题结束,给出核心代码片段
C++
string shortestPalindrome(string s)
{
string rev = s;
std::reverse(rev.begin(), rev.end());
string t = s + "$" + rev;
vector<int> pi = buildPi(t);
int longestPrefixLen = pi.back();
string rest = s.substr(longestPrefixLen);
std::reverse(rest.begin(), rest.end());
return rest + s;
}