LeetCode214 最短回文串

原题目

在这里插入图片描述

代码分析

题目可理解为找最长回文前缀,将不满足的部分翻转添加在首部即可
如:
例1:aacecaaa
回文前缀为aacecaa 剩余a
为使其构成回文添加a
形成aaacecaaa
例2:abcd
最长前缀为a,剩余bcd
为使其构成回文添加dcb
形成dcbabcd

难点:最长回文前缀怎么找

方法一:暴力法

先翻转原字符串,找最长前缀回文串,就是找原字符串的前缀和翻转字符串的后缀能匹配的最大长度

例1:原 aacecaa a
翻转 a aacecaa
翻转字符串前剩余的添加到源字符串前面形成aaacecaaa

例2: 原 a bcd
翻转 dcb a
翻转字符串前剩余的添加到源字符串前面形成dcbabcd

比较时,不断缩小原字符串的前缀和翻转字符串的后缀,直到相等为止

class Solution {
public:
    string shortestPalindrome(string s) {
        int len = s.size();
        string rev(s.rbegin(),s.rend());//翻转
        string temp1 = s,temp2=rev;//temp1 s的前缀,temp2 rev的后缀
        for(int i = 0; i < s.size(); i++){
            if(temp1==temp2){
                return rev.substr(0,i)+s;
            }
            temp1.pop_back();
            temp2.erase(temp2.begin());
        }
        return "";
    }
};
方法二:递归

找最长回文前缀
如果我们可以减小字符串的大小来搜索子字符串,而是不每次都检查完整的子字符串,就可以使该过程更加高效。

例如字符串“abcbabcaba”。 考虑两个指针 i 和 j。初始化 i = 0。jj 从 n−1 到 0 迭代,每当s[i]==s[j] 时,将 i 增加 1。现在,我们只需要 在[0,i) 范围内搜索。这样,我们就缩小了从开头寻找最大回文子串的搜索长度。[0,i) 必然包括最大回文子串。其正确性证明如下:若字符串全部为回文,则i会自增n次。若结尾有其他字符,i 会自增回文子串的长度。于是,即使 [0,i) 并不总是紧界,但它总包含从开头开始的最长回文子串。

简单来说就是不连续匹配前后字符,找到最长能匹配的长度,不断缩减,直到成为回文即可
例1:原 aacecaaa
翻转 aaacecaa
递归1:
 aacecaaa
i = 6
返回 翻转最后不符合回文前缀的字符+递归前6个字符+最后不符合回文前缀的字符
递归2:
 aacecaa
i = 6
返回当前回文字符串

结果a + aacecaa + a

class Solution {
public:
    string shortestPalindrome(string s) {
        int  i = 0;
        string rev(s.rbegin(),s.rend());
        for(int j = s.size()-1; j>=0;j--){
            if(s[i] == s[j]){
                i++;
            }
        }
        if(i == s.size()){
            return s;
        }
        return rev.substr(0,s.size()-i)+shortestPalindrome(s.substr(0,i))+s.substr(i);
    }
};
方法三:KMP

后缀找前缀的最大匹配字符,想到了KMP算法中的next函数
我们要找原字符串的前缀和翻转字符串的后缀能匹配的最大长度,可以将原字符串和翻转字符串合并,利用KMP找到最后一个字符能匹配到的最大前缀,因为前缀最大为原字符串长度,在中间加个#就不会找到比原字符串长的前缀了
例1:原 aacecaaa
翻转 aaacecaa
aacecaaa#a aacecaa
      aacecaa a#aaacecaa

aacecaaa#aaacecaa
-101000122012234567

最长前缀匹配数为7,将翻转字符串剩余字符添加在原字符串首部

例2: 原 abcd
  翻转 dcba
abcd#dcb a
      a bcd#dcba

abcd#dcba
-1000000001

最长前缀匹配数为1,将翻转字符串剩余字符添加在原字符串首部

class Solution {
public:
    string shortestPalindrome(string s) {
        //KMP
        string rev(s.rbegin(),s.rend());
        string str = s + "#" + rev;
        vector<int>next(str.size()+1);
        next[0] = -1;
        int i = 0, j = -1;
        
        while(i<str.size()){
            if(j == -1||str[i] == str[j]){
                i++;
                j++;
                next[i] = j;
            }else{
                j = next[j];
            }
        }
        
        return rev.substr(0,s.size()-next[str.size()])+s;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baal Austin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值