LeetCode------最长回文子串

https://leetcode-cn.com/problems/longest-palindromic-substring/

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

四种算法:

一、暴力法

双指针双重循环切分字符串,双指针查找对比。

时间复杂度O(n^3)

char* longestPalindrome(char* s) {
    int length = strlen(s),l =0,r = length-1,index1=0,index2=0,max = 0,n = 0,flag=1;
    char *p=NULL;
    if(length<=1) return s;
    for(int i = 0;i<length-1;i++){    //i,j切分出所有可能字符串
        for(int j = i+1;j<length;j++){
            flag = 1,n=0;
            for(int k = i,m = j;k<=m;k++,m--){ //验证该字符串是否是回文
                if(s[k]!=s[m]){           //如果有不同,则不是回文
                    flag = 0;
                    break;
                }
                if(m!=k) n+=2;
                else n++;
            }
            if(flag){                  //若是回文,且长度大于已知最长,记录下标
                if(n>max){
                    max = n;
                    index1 = i;
                    index2 = j;
                }
            }
        }
    }
    p = (char*)malloc(sizeof(char)*(index2-index1+2));//复制回文串
    for(int i = index1;i<= index2;i++){
        p[i-index1] = s[i];
    }
    p[index2-index1+1]  ='\0';
    return p;
}//时间复杂度O(n^3)空间复杂度O(1)

二、中心拓展算法

n个字符串有2*n-1个回文中心,比如abbab,对于单个字符,中心是它本身,如a,对于双字符,中心是空,如bb。

只要从中心向两端匹配,就能匹配出当前回文串的长度。

int expand(char *s,int left,int right,int length);
char* longestPalindrome(char* s) {
    int length=strlen(s),l=0,r=0;
    char *p=NULL;
    for(int i = 0;i < length;i++){        //最基本的回文单位是单字符或者相同双字符。
        int len1 = expand(s,i,i,length);  //遍历所有单字符
        int len2 = expand(s,i,i+1,length); //遍历所有连续双字符
        int len = len1 > len2 ? len1 : len2;
        if(len > r - l){            //这句话等于(len>=r-l+1)而r-l+1为同样长的字符串
            l = i - (len-1)/2;      //根据中点值,计算左右界限
            r = i + len/2;
        }
    }
    p = (char *)malloc(r-l+2);    //复制数组
    for(int i = l;i<=r;i++){
        p[i-l]=s[i];
    }
    p[r-l+1] = '\0';
    return p;
}
int expand(char *s,int left,int right,int length){  //拓展函数,返回长度
    int l = left,r = right;
    while(l>=0 && r<length && s[l]==s[r]){
        l--;
        r++;
    }
    return r-l-1;  //当前值已经不满足,实际上满足的下标是 l+1和r-1,最终长度为r-l-1
}时间复杂度O(n^2) 空间复杂度O(1)

三、动态规划

设置辅助数组dp[length][length],以若从起始点为i,结束点为j,且i,j范围内的字符串是回文,那么dp[i][j]=1。

边界条件,dp[i][i] = 1,dp[i][i+1] = dp[i] == dp[i+1].

状态转移方程:dp[i][j] = dp[i+1][j-1] && dp[i]==dp[j];

char* longestPalindrome(char* s) {
    int length = strlen(s),max = 0,index1=0,index2=0;
    char **p=NULL,*p1=NULL;
    p = (char **) malloc(sizeof(char*)*length);
    for(int i = 0;i<length;i++){
        p[i] = (char*)malloc(sizeof(char)*length);
    }
    for(int i = 0;i<length;i++){   //dp前先做好基本准备
        p[i][i]=1;
        if(i<length-1){
            if(s[i]==s[i+1]){
                p[i][i+1]=1;
                max = 2;
                index1 = i;
                index2 = i+1;
            }
        }
    }
    for(int i = 3;i<=length;i++){     //根据长度dp,这样无需与最大值比较
        for(int j=0;i+j-1<length;j++){  //j为起始点,k为终点
            int k = i+j-1;
            if(s[j]==s[k] && p[j+1][k-1]==1){ //状态转移方程
                p[j][k] = 1;
                max = i;
                index1 = j;
                index2 = k;
            }
            
        }
    }
    p1 = (char*)malloc(index2-index1+2);  //复制数组
    for(int i = index1;i<=index2;i++){
       p1[i-index1] = s[i];
    }
    p1[index2-index1+1] = '\0';
    return p1;
}时间复杂度O(n^2) 空间复杂度O(n^2)

四、Manacher算法

请自行阅读相关文献

char* longestPalindrome(char* s) {
    int length = strlen(s),*len=NULL;
    int mid=1,mx=0,max_mid=0,max = 0;
    char *p=NULL,*p2=NULL;
    if(length==0) return s;
    p = (char *)malloc(length+length+3);
    p[0] = '$';
    p[1] = '#';
    for(int i=0,j=2;i<length;i++){
        p[j++] = s[i];
        p[j++] = '#';
        if(i==length-1) p[j] = '\0';
    }                                    //构建新字符串
    length = strlen(p);                  //重置长度
    len = (int *)malloc(sizeof(int)*(length));//辅助数组
    for(int i=0;i<length;i++) len[i] = 0;//可以不初始化
    for(int i = 1;i<length;i++){
        if(i<mx) len[i] = len[2*mid-i] < mx-i? len[2*mid-i]:mx-i; //如果当前值在最右极点的右左边
        else len[i]=1;       //有两种情况,一是左对称点的长度在左极点内,这样可以确定对称点长度相同
        while(p[i+len[i]]==p[i-len[i]]){//另一种情况是对称点长度越出左极点,这样确定极点长度内的
            len[i]++;                //元素一定是回文串,接下来不断对比左右,拓展已知长度、
        }
        if(i+len[i]>mx){      //若新的右界大于当前右界,则重置中点,重置右界
            mx = i+len[i];            
            mid = i;
        }
        if(len[i]>max){   //记录最长串中点和半径
            max = len[i];
            max_mid = i;
        }
    }
    p2 = (char *)malloc(max+2);  //将回文串复制到新的数组。
    for(int i = max_mid - max +2,j=0;i<=max_mid+max-2;i++){
        if(p[i]=='#') continue;
        else{
            p2[j++] = p[i];
        }
        if(i==max_mid+max-2) p2[j]='\0';
    }
    return p2;          //返回
}//时间复杂度O(n) 空间复杂度O(n)

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值