Bootstrap

力扣 214. 最短回文串 字符串hash/KMP算法

https://leetcode-cn.com/problems/shortest-palindrome/
在这里插入图片描述
思路:思路其实很简单,因为只能在字符串的头部添加字符,所以我们只要找到下标从0开始的最长回文串 s 1 s_1 s1,假设后面部分为 s 2 s_2 s2,那么答案就等于 r e v e r s e ( s 2 ) + s reverse(s_2)+s reverse(s2)+s。怎么在线性复杂度内找到 s 1 s_1 s1呢?我们有两种方法,第一种方法是利用 h a s h hash hash,通过定义进制数和模数,我们可以将一个字符串表示成数字,比如进制为 10 10 10,模数为 100 100 100时, h a s h ( b c d ) = ( 100 ∗ 1 + 10 ∗ 2 + 1 ∗ 3 ) % 100 = 23 hash(bcd)=(100*1+10*2+1*3)\%100=23 hash(bcd)=(1001+102+13)%100=23。再考虑回文串的性质:如果 s s s是一个回文串,且 t = r e v e r s e ( s ) t=reverse(s) t=reverse(s),那么有 t = s t=s t=s。那么自然有 h a s h ( t ) = h a s h ( s ) hash(t)=hash(s) hash(t)=hash(s),这样我们就可以在 O ( n ) O(n) O(n)内求出最长的回文串 s 1 s_1 s1啦,但是哈希是有可能发生碰撞的,也就是说两个不相等的字符串的哈希值一样,为了避免这种情况,我们一般使用一个质数作为进制数,同时选取另一个大质数作为模数。下面给出方法一的代码:

class Solution {
public:
    using ll=long long;
    string shortestPalindrome(string s) {
        int siz=s.size();
        int base=131,mod=1e9+7;
        int idx;
        ll hash1=0,hash2=0,rightbs=1;
        for(int i=0;i<siz;i++){
            hash1=(hash1*base+s[i]-'a')%mod;
            hash2=(hash2+rightbs*(s[i]-'a'))%mod;
            rightbs=(rightbs*base)%mod;
            if(hash1==hash2)
                idx=i;//说明[0,idx]构成一个回文串
        }
        //得到最大的idx 开始拼接
        string tmp=siz?s.substr(idx+1,siz-idx-1):"";
        reverse(tmp.begin(),tmp.end());
        return tmp+s;
    }
};

方法二需要用到 K M P KMP KMP算法。我们假设 t = r e v e r s e ( s ) t=reverse(s) t=reverse(s),把 s s s作为模式串, t t t作为查询串,当遍历到 t t t的末尾时,易得模式串指针 j j j的值,也就是说 s [ 0 … j ] = t [ n − j − 1 , n − 1 ] s[0…j]=t[n-j-1,n-1] s[0j]=t[nj1,n1],由于 t = r e v e r s e ( s ) t=reverse(s) t=reverse(s),所以可以得到 s [ 0 … j ] = s [ j … 0 ] s[0…j]=s[j…0] s[0j]=s[j0],也就是说 s [ 0 … j ] s[0…j] s[0j]是下标从 0 0 0开始的最长的回文串,那么问题就解决辣。

class Solution {
public:
    using ll=long long;
    string shortestPalindrome(string s) {
        int siz=s.size();
        if(!siz)
            return s;
        vector<int> next(siz);
        int k=-1,i=0,j=0;
        next[0]=-1;
        while(i<siz-1){ //kmp--求next数组
            if(k==-1||s[i]==s[k]){
                if(s[++i]==s[++k])
                    next[i]=next[k]; //优化
                else
                    next[i]=k;
            }
            else
                k=next[k];
        }
        i=siz-1;//reverse(s)作为查询串 所以i要逆序扫
        while(i>=0){
            if(j==-1||s[i]==s[j])
                --i,++j;
            else
                j=next[j];
        }
        string tmp=s.substr(j,siz-j);
        reverse(tmp.begin(),tmp.end());
        return tmp+s;
    }
};
;