214. Shortest Palindrome

来源:互联网 发布:小码哥java培训怎么样 编辑:程序博客网 时间:2024/05/18 09:11

这道题给出一个字符串,要把它改成一个回文串的话,需要在前面加几个字符,要求写出这个回文串。

看到这个题首先想到的是,我可以用动态规划求出从0开始最长的回文串,然后把后面的部分翻转加到前面,但是超时了。

看了别人的思路,发现大家都是用的KMP,想了很久才明白了个大概,现在把思路记录一下省的忘了。

首先,为什么会想到用KMP呢?

我们的目标还是找到s中以s[0]开头的最长的回文串。如果一个字符串的前缀不是回文的,那么把他倒过来加在字符串后面作为后缀,一定和前缀不一样的。如果前后缀是相同的字符串,那么这个字符串肯定是回文的。所以我们找最长的前后缀就是最长的回文串了。那么找最长的前后缀自然是用KMP算法的next数组。

然后,构造一个字符串。

根据前面的分析,我们要把s翻转变为s_r,把s_r加在s的后面,但不是连起来就好了,需要在他们中间加上#作为分隔符,为什么要加#,考虑s="aaaa"字符串,如果把他反过来加在s的后面,那前后缀就变成了"aaaaaaaa",显然超出了s的长度,肯定不对,所以要加上个#,找后缀的时候找到s_r的开头就不要再往前找了。

然后,next数组。

next[i]表示的是0~i这一段字符串最长的相同前后缀长度是多少求法可以参看http://blog.csdn.net/v_july_v/article/details/7041827

KMP算法有一个理解上的难点:已知next[q-1] = k,看下图的前两行,这个式子的意思就是,p[q-k]...p[q-1] = p[0]...p[k-1]。等号左边是p[0]...p[q-1]这个字符串的后缀,等号右边是这个字符串的前缀。现在要求next[q]等于多少。如果p[q]等于p[k],那么next[q] = k+1。这个好理解。难点是,如果p[q] != p[k]呢?这时候怎么找p[0]...p[q]的前缀和后缀呢?我们可以看成把下图的第二行往右移动。如果移动了一段之后发现,p[q] == p[j]且p[0]...p[j-1] == p[q-j]...p[q-1],这时候就找到了next[q],那next[q]是多少呢,肯定是j+1,j是多少?看图的后两行,我们发现p[0]...p[j-1]是p[0]...p[k-1]的前缀,也就是说,j = next[k]。也就是说,如果前缀后缀匹配不成功了,就找前缀的前缀。如果前缀的前缀还没匹配成功(也就是这个图里p[q] != p[j]),那就再找前缀的前缀的前缀!

这是反过来想的,可能有人看着更糊涂了。那正过来想,如果现在要求next[q],但是p[k] != p[q],那么匹配不成功了,那第二个字符串肯定要往右移动。我们的要求是两个条件:(1)p[q-j]~p[q-1] == p[0]~p[j-1] (2)p[q] == p[j]。那第二个字符串要往右移动多少呢,移动一个字符的位置?那这时候p[q-k+1]~p[q-1] != p[0]~p[k-2]啊,第一个条件不满足,就更别提第二个条件了。要满足第一个条件,那一定要移动到让p[0]~p[k-1]的前缀对准p[0]~p[k-1]的后缀。也就是第二行到第三行图里这个样子。p[0]~p[j-1]是p[0]~p[k-1]的前缀,让他对准p[0]~p[k-1]的后缀。这样第一个条件就满足了,然后这时候看看p[j]是不是等于p[q]。


最后,得到结果。

next.back()表示的是新字符串的最长前后缀长度,也就是原字符串s从s[0]开头的最长回文串的长度。从s_r中截取开头的s.size()-next.back()长度的子串,加到s的前面,就是结果了。

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;    }};


原创粉丝点击