马拉车算法 LeetCode-5.最长回文子串、LeetCode-214.最短回文串

马拉车算法

马拉车算法是一种寻找给定字符串中的最长回文子串的方法,时间复杂度为 O(n) 。它除了可以解决“最长回文子串”的问题,还能解决由此变形的一些衍生问题,如“最短回文串”

这里是马拉车算法的详述:
一文让你彻底明白马拉车算法

LeetCode-5.最长回文子串

这里是题目描述:LeetCode-5.最长回文子串

O(n)时间复杂度马拉车算法:

马拉车算法解决的就是这个问题,因此不再做详述
马拉车算法题解代码:

class Solution {
    //马拉车算法
    public String longestPalindrome(String s) {
        String T=preProcess(s);
        int[] P=new int[T.length()];
        int c=0,r=0;
        for(int i=1;i<T.length()-1;i++)
        {
            int i_mirror=2*c-i;
            if(r>i)
            {
                P[i]=Math.min(r-i,P[i_mirror]); //防止超出r
            }
            else
            {
                P[i]=0; //r==i的情况
            }

            //碰到之前讲的三中情况,需要使用中心扩展法
            while(T.charAt(i+1+P[i])==T.charAt(i-1-P[i]))
            {
                P[i]++;
            }

            //判断是否需要更新r
            if(i+P[i]>r)
            {
                c=i;
                r=i+P[i];
            }
        }
        //找出P的最大值
        int maxLen=0;
        int centerIndex=0;
        for(int i=0;i<P.length;i++)
        {
            if(maxLen<P[i])
            {
                maxLen=P[i];
                centerIndex=i;
            }
        }
        int startIndex=(centerIndex-maxLen)/2; //原字符串最长回文子串的开始下标
        return s.substring(startIndex,startIndex+maxLen);
    }
    String preProcess(String s) //预处理
    {
        if(s.length()==0)
        {
            return "^$";
        }
        StringBuffer sb=new StringBuffer();
        sb.append('^');
        for(int i=0;i<s.length();i++)
        {
            sb.append('#');
            sb.append(s.charAt(i));
        }
        sb.append("#$");
        return sb.toString();
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

O(n2)时间复杂度动规方法:

本题可以还用动态规划的方法来解:维护一个二维表dpdp[i][j]存储给定字符串s的子串s[i:j]所包含的最长回文子串长度。状态转移方程:

dp[i][j]=1                                       //i==j || (j=i+1 && s[i]!=s[j])
dp[i][j]=2                                       //i=i+1 && s[i]==s[j]
dp[i][j]=dp[i+1][j-1]+2                          //s[i]==s[j] && dp[i+1][j-1]==(j-1)-(i+1)+1
dp[i][j]=max(dp[i+1][j],dp[i][j-1])              //其他情况

这个方法的时间复杂度:O(n2),空间复杂度:O(n2)
动规方法题解代码:

class Solution {
    public String longestPalindrome(String s) {
        if(s.length()<=1)
        {
            return s;
        }
        int[][] dp=new int[s.length()][s.length()];
        for(int i=0;i<s.length();i++) //初始化子串长度为1的情况
        {
            dp[i][i]=1;
        }
        for(int i=0;i<s.length()-1;i++) //初始化子串长度为2的情况
        {
            if(s.charAt(i)==s.charAt(i+1))
            {
                dp[i][i+1]=2;
            }
            else
            {
                dp[i][i+1]=1;
            }
        }
        for(int l=3;l<=s.length();l++)
        {
            for(int i=0;i<s.length()-l+1;i++)
            {
                int j=i+l-1;
                if(s.charAt(i)==s.charAt(j) && dp[i+1][j-1]==l-2)
                {
                    dp[i][j]=l;
                }
                else
                {
                    dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]);
                }
            }
        }
        int r=0,l=s.length()-1;
        while(dp[r][l]!=(l-r+1))
        {
            if(dp[r+1][l]==dp[r][l])
            {
                r=r+1;
            }
            else
            {
                l=l-1;
            }
        }
        return s.substring(r,l+1);
    }
}

LeetCode-214.最短回文串

这里是题目描述:LeetCode-214.最短回文串

示例1:
输入: "aacecaaa"
输出: "aaacecaaa"

示例2:
输入: "abcd"
输出: "dcbadcd"

在示例1中,我们需要输入字符串'aacecaaa'前面添加'a'得到回文串'aaacecaaa',能够以这种方法得到的最短的字符串;可以通过观察得知,字符串'aacecaaa'的以最左边的字符为开头的最长回文子串是'aacecaa',还剩下最右边的'a',因此在前面添加'a'就能得到最短回文串'aaacecaaa'
马拉车算法 LeetCode-5.最长回文子串、LeetCode-214.最短回文串_第1张图片
因此本题可以转化为求“输入字符串中包含的以最左边的字符为开头的最长回文子串”,求得该最长回文子串后,将字符串右边不是回文子串的剩余子串的逆序添加到字符串左边,就得到了最短回文串。示例2同样可以用这种方法解:
马拉车算法 LeetCode-5.最长回文子串、LeetCode-214.最短回文串_第2张图片而求“输入字符串中包含的以最左边的字符为开头的最长回文子串”这一步就可以交给马拉车算法来完成

题解代码:

class Solution {
    //基于马拉车算法
    public String shortestPalindrome(String s) {
        String T=preProcess(s);
        int[] P=new int[T.length()];
        int c=0,r=0;
        for(int i=1;i<T.length()-1;i++)
        {
            int i_mirror=c*2-i;
            if(r>i)
            {
                P[i]=Math.min(r-i,P[i_mirror]); //防止超出r
            }
            else
            {
                P[i]=0; //r==i的情况
            }

            //碰到三种特殊情况,使用中心扩散法
            while(T.charAt(i+1+P[i])==T.charAt(i-1-P[i]))
            {
                P[i]++;
            }

            //判断是否需要更新r
            if(r<i+P[i])
            {
                c=i;
                r=i+P[i];
            }
        }

        //寻找对应的原字符串开头是第一个字符的最大P
        int maxLen=0;
        for(int i=0;i<P.length;i++)
        {
            if(maxLen<P[i] && (i-P[i])/2==0)
            {
                maxLen=P[i];
            }
        }
        StringBuffer sb=new StringBuffer();
        for(int i=s.length()-1;i>=maxLen;i--)
        {
            sb.append(s.charAt(i));
        }
        sb.append(s);
        return sb.toString();
    }
    String preProcess(String s) //预处理
    {
        if(s.length()==0)
        {
            return "^$";
        }
        StringBuffer sb=new StringBuffer();
        sb.append('^');
        for(int i=0;i<s.length();i++)
        {
            sb.append('#');
            sb.append(s.charAt(i));
        }
        sb.append("#$");
        return sb.toString();
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

你可能感兴趣的:(马拉车算法 LeetCode-5.最长回文子串、LeetCode-214.最短回文串)