Shortest Palindrome ---再见回文和KMP

题目大意

    给定串S,要求在其前面(in front of)增加最少的串构成回文并输出回文串。

解题思路

     席八,这个题目无愧其hard的标签,我想破脑袋也才想了个宽搜的暴力,而且还忽略了要加载S串前面的基本要求,审题不仔细,还是太浮躁了。这题没做出来,仔细琢磨了网上的解法,现总结如下:

    先明确几个理论,以后想回文相关的题目从这几点入手:

                                      1、回文串 = reverse(回文串)

                                      2、回文串T+回文串T 为了减少长度可以压缩视为为一个回文串T

                                      3、任何字符串S 经过镜面翻转必为回文串

   以上三点看上去都特么是废话,但就是这种简单的常识往往难以被重视。回到这个题目,首先尝试构造一个最坏的解,根据第三点,将S串翻转,加到其前(为什么不是后面?因为题目要求要在前面加嘛。。。众人:碧池~~)。这样显然用最差的方式构建了一个长度为2*s.length()的回文串,因此需要对其进行优化,此时考虑上述第二点,如果S前缀和后缀都是回文串的话,这样翻转之后的串就能够被压缩了。。注意这里出现了前缀和后缀,前缀和后缀,前缀和后缀,重要的事情讲三遍,是的,前缀和后缀(众人:还来?滚粗!说重点),由此很是不是很容易想到通过KMP的next串的构造找到前缀和后缀相同的长度?因此重新构造字符串T=原串S,将T镜面翻转到后面构成T’(为什么这次翻转到后面,因为需要检测S前缀中回文长度,根据第一点,利用KMP匹配出来的前缀肯定是回文),这样通过KMP可以得出其前缀最长回文长度,最后只需要将原串中非回文的部分(长度为s.length()-next[T‘.length()-1])的部分翻转到串的最前面就可以满足题目的要求了,整体来讲应该是一种贪心的思路。

补充:

这里有个细节需要更正一下,首先感谢田神要努力填坑同志能发现这一问题,并给出合适的测试样例aabba。该算法存在一个细节问题,执行KMP匹配的时候不应当进行重复的匹配,这样会导致判断前缀回文的判断超过串本身的长度(因为是直接翻转后放置在后面,故而计算next数组时会将翻转串的部分也会比对进来),因此应当将翻转串和原串分开比对,略作修改就好

总结

    首先题目要看懂,其次但凡涉及回文就要考虑上述三点基本特征

代码(存在问题)

class Solution {
	int next[81000];
	void generateNext(string s)
	{
		  next[0]  = 0;
          for (int i = 1, j = 0; i < s.length(); i ++) {
	       while (j > 0 && s[j] != s[i])  j = next[j-1]; 
               if (s[i] == s[j])  j ++;
               next[i] = j;
          }
	}
public:
    string shortestPalindrome(string s) {
		int sLen = s.length();
		if (sLen == 0)return "";
		string cur = s;
		string rs = s;
		reverse(rs.begin(), rs.end());
		cur += rs;
		int curN = cur.length();
                generateNext(cur);
                int commonLen = next[curN-1];
		if (commonLen > sLen)return s;
		string res = s.substr(commonLen,sLen-commonLen);
		reverse(res.begin(), res.end());
		return (res+s);
    }
};

代码(修改上段代码的错误)

<span style="font-size:14px;">class Solution {  
    int next1[41000];
	int next2[41000];
    void generateNext(string s1, string s2)  
    {  
          next1[0] = 0;
		  next2[0] = 0;  
          int len1 = s1.length();
          for (int i = 1, j = 0; i < len1; i ++) {  
           	   while (j > 0 && s1[j] != s1[i])  j = next1[j-1];   
               if (s1[i] == s1[j])  j ++;  
               next1[i] = j;  
          }  //这里使用翻转串s2去匹配原串s1
          for (int i = 0, j = 0; i < len1; i ++) {  
               while (j > 0 && s2[i] != s1[j])  j = next1[j-1];   
               if (s2[i] == s1[j])  j ++;  
               next2[i] = j;  
          }  
    }  
public:  
    string shortestPalindrome(string s) {  
        int sLen = s.length();  
        if (sLen == 0)return "";  
        string cur = s;  
        string rs = s;  
        reverse(rs.begin(), rs.end());  
        //cur += rs;  
        int curN = cur.length();  
        generateNext(cur, rs);  
        int commonLen = next2[curN-1];  
        if (commonLen > sLen)return s;  
        string res = s.substr(commonLen,sLen-commonLen);  
        reverse(res.begin(), res.end());
        return (res+s);  
    }  
}; </span>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值