[dp][马拉车算法][中心扩展法]leetcode5:最长回文子串(medium)

题目:
[dp][马拉车算法][中心扩展法]leetcode5:最长回文子串(medium)_第1张图片
题解:

  • ①:动态规划
  • dp[i][j]表示s[i]至s[j]所表示的子字符串是否为回文串,是则为1,不是则为0。这样可根据s[i]是否等于s[j],可以把转移情况分为两类:
  • 1)若s[i]==s[j],那么只要s[i+1]至s[j-1]是回文串,那么s[i]至s[j]就是回文串;如果s[i+1]至s[j-1]不是回文串,则s[i]至s[j]就不是回文串。
  • 2)若s[i]!=s[j],那么s[i]至s[j]一定不是回文子串。
  • 状态转移方程为:若s[i]==s[j]dp[i][j]=dp[i+1][j-1];若s[i]!=s[j]dp[i][j]=0
  • 边界条件:dp[i][i]=1dp[i][i+1]=(s[i]==s[i+1])?1:0

  • ②:马拉车算法
  • 马拉车算法详解

  • ③:中心扩展法
  • 由于回文子串关于最中间元素对称,所以我们对于每一个遍历到元素使用中心扩展法,寻找到回文串的长度,然后确定更不更新最长回文串长度。注意:由于存在奇回文串和偶回文串,所以我们会使用一个函数来回文串最中间一个字符来扩展(针对奇回文串)或者从最中间的两个字符来扩展(针对偶回文串)。

代码如下:

class Solution {
public:
    //解法1:动态规划
    string longestPalindrome_1(string s) {
        if(s.size()<2)return s;
        
        //dp[i][j]表示s[i]至s[j]是否为回文串,是为1,不是为0
        int size=s.size(),dp[size][size];
        memset(dp,0,sizeof(dp));
        
        int max=1,start=0;
        
        //初始化边界值
        for(int i=0;i<size;++i){
            dp[i][i]=1;
            if(i<size-1&&s[i]==s[i+1]){
                dp[i][i+1]=1;//是回文串,表示为1
                max=2;
                start=i;
            }
        }
        
        for(int l=3;l<=size;l++)//l表示检索的子串长度,等于3表示先检索长度为3的子串
        {
            for(int i=0;i+l-1<size;i++)
            {
                int j=l+i-1;//终止字符位置
                if(s[i]==s[j]&&dp[i+1][j-1]==1)//状态转移
                {
                    dp[i][j]=1;
                    start=i;
                    max=l;
                }
            }
        }
        return s.substr(start,max);
    }
    
    //解法2:马拉车算法
    string longestPalindrome_2(string s)
    {
    	//插入"#"
        string t="$#";
        for(auto c:s){
            t+=c;
            t+='#';
        }
        
        vector<int> p(t.size(),0);
        
        //mx表示某个回文串延伸在最右端半径的下标,id表示这个回文子串最中间位置下标
    	//resLen表示对应在s中的最大子回文串的半径,resCenter表示最大子回文串的中间位置
        int mx=0,id=0,resLen=0,resCenter=0;
        
        //建立p数组
        for(int i=1;i<t.size();++i)
        {
        	//更新p[i]的值
            p[i]=mx>i?min(p[2*id-i],mx-i):1;
            
            //遇到三种特殊的情况,需要利用中心扩展法
            while(t[i+p[i]]==t[i-p[i]])p[i]++;
            
            //半径下标i+p[i]超过边界mx,需要更新
            if(mx<i+p[i]){
                mx=i+p[i];
                id=i;
            }
            
            //更新最大回文子串的信息
            if(resLen<p[i]){
                resLen=p[i];
                resCenter=i;
            }
        }
        
        //最长回文子串长度为半径-1,起始位置为中间位置减去半径再除以2
        return s.substr((resCenter-resLen)/2,resLen-1);
    }
    
    //解法3:中心扩展法
    string longestPalindrome(string s){
        if(s.size()<2)return s;
        
        //回文串的起始和终止位置以及最大回文串的长度
        int start=0,end=0,mLen=0;
        
        for(int i=0;i<s.size();++i)
        {
            int len1=expand(s,i,i);//以i为中心向两边扩展
            int len2=expand(s,i,i+1);//以两个元素为中心向两边扩展
            
            mLen=max(max(len1,len2),mLen);//更新最大回文串长度
            
            if(mLen>end-start+1){//更新回文串的起始和终止位置
                start=i-(mLen-1)/2;
                end=i+mLen/2;
            }
        }
        //返回最长回文子串的起始位置+长度
        return s.substr(start,mLen);
    }
    
private:
	//中心扩展函数,注意这里string参数用引用,避免拷贝浪费时间
    int expand(string& s,int left,int right){
        while(left>=0&&right<s.size()&&s[left]==s[right]){
            left--;
            right++;
        }
        return right-left-1;
    }
};

你可能感兴趣的:(leetcode刷题,#,动态规划,#,字符串)