最长回文子串C语言

1.如何判断回文?
aba abba分奇数偶数;
2.题型

a:无重复字符的最长子串,利用hash[128]统计每个字符,双指针left 和right,如果hash[s[right]] == 0 就是没出现,计数++,right++;hash[s[right]] = 1;比较res和cnt最大值;
如果hash[s[right]] != 0,hash【left】 = 0;cnt–,left++;
b: 最长回文串
在这里插入图片描述

// 其实这是个数学题,通过给定的字符串s,构造出来的最长回文字符串,长度肯定小于等于s,如果小于s,那么最长回文串b的长度必然是奇数。
int longestPalindrome(char * s)
{
    int c[128]={0};
    int ret=0;
    
    for(int i=0;i<strlen(s);i++)
    {
        c[s[i]]++;
    }

    for(int i=0;i<128;i++)
    {
        if (c[i] % 2 == 0){
            ret =ret + c[i];
        }else {
            ret = ret + (c[i] -1);//-1
        }
    }

    if( ret != strlen(s)){
        ret = ret + 1;
    }
    return ret;
}

题目描述
在这里插入图片描述
解法一:常规暴力解法:
遍历字符串
1.对于每个字母,向两侧扩散,判断是否回文子串;
2.若为回文子串,保存最长的子串信息
3.子串长度为奇数或偶数,需分别判断

char * longestPalindrome(char * s){
    int N = strlen(s), start = 0, len = 0;  // N 字符串长度, start 子串起始位置, len 子串长度
    for (int i = 0; i < N; i++) {   // 奇数长度的回文子串
        int left = i - 1, right = i + 1;
        while (left >= 0 && right < N && s[left] == s[right]){
            left--; right++;            // 以 i 为中心,向两侧延伸,直到不再满足回文
        }                               // left+1 ~ right-1 则为以i为中心的最大回文子串
        if (right - left - 1 > len) {   // 若更长,则保存该子串信息
            start = left + 1;
            len = right - left - 1;
        }
    }
    for (int i = 0; i < N; i++) {   // 偶数长度的回文子串
        int left = i, right = i + 1;    // 以 i+0.5 为中心,向两侧延伸
        while (left >=0 && right < N && s[left] == s[right]) {
            left--, right++;
        }
        if (right - left - 1 > len) {
            start = left + 1;
            len = right - left - 1;
        }
    }
    s[start + len] = '\0';      // 原地修改返回
    return s + start;
    }

解法二:动态规划
使用动态规划,依次检测长度为 2,3,4…s.length 的字符串是否为回文串。
1.对于字符串长度为 1 的字符串不用检测,一定是回文串;
2.对于字符串长度为 2,3 的字符串,只需要首尾字符相同即为回文串;
3.对于字符串长度大于 3 的字符串,则检测首尾字符,若相同,则与去掉首尾字符的字符串结果相同(保存在dp中)

char * longestPalindrome(char * s){
    //回文串结束下标
    int j;
    //最长回文串长度
    int maxLength = 1;
    //回文串起始位置
    int begin = 0;
    int len = strlen(s);
    // dp[i][j] 表示 s[i..j] 是否是回文串
    bool dp[len][len];

    //0.初始化:所有长度为 1 的子串都是回文串 
    for (int i = 0; i < len; i++) { 
        dp[i][i] = true; 
    }

    //1.长度为 1 的一定是回文串,故长度从 2 开始
    for(int l = 2; l <= len; l++){
        //2.起始位置从 0 开始
        for(int i = 0; i < len; i++){
            ///结束位置
            j = i + l - 1;

            //3.注意数组越界,注意 等于
            if (j >= len) {
                break;
            }

            //回文串首尾字母不相同
            if (s[i] != s[j]) {
                dp[i][j] = false;
            } else {
                //回文串小于 3 个字符( 3 个字符内首尾相等即为回文串)
                if (j - i < 3) {
                    dp[i][j] = true;
                } else {
                    //回文串大于两个字符
                    dp[i][j] = dp[i + 1][j - 1];
                }
            }

            //4.只要 dp[i][L] == true 成立,就表示子串 s[i:L] 是回文,此时记录回文 长度 和 起始位置
            if (dp[i][j] && l > maxLength) {
                maxLength = l;
                begin = i;
            }
        }
    }
    //设置结束位置
    s[begin + maxLength] = '\0';
    return s + begin;
}

解法三:中心扩散
对字符串中每一个元素遍历,并进行中心扩散,同时记录最大值

/*
    对当前中心扩散
    @s :要判断的字符串
    @left:中心往左走
    @right:中心往有走
    @ans:保存最长结果
*/
void extend(char *s,int left,int right,int* ans)
{
    //如果越界直接跳出
    if(left<0 && right>=strlen(s))
    {
        return ;
    }
    //进行中心扩散
    //如果没有越界并且当前位置两个字符串相等则将left左移right右移
    while(left>=0 && right<strlen(s) && s[left]==s[right])
    {
        left--;
        right++;
    }
    //记录最大值
    if(right-left > ans[1]-ans[0])
    {
        ans[1] = right;
        ans[0] = left;
    }
    return ;
}
char * longestPalindrome(char * s){
    int ans[2] = {0,0};
    for(int i = 0; i < strlen(s); i++)
    {
        extend(s,i,i,ans);//一个值为中心的情况
        extend(s,i,i+1,ans);//两个值为中心的情况
    }
    //保存返回值
    char* r = (char*)malloc(ans[1]-ans[0]);
    strncpy(r, s+ans[0] + 1 ,ans[1] - ans[0] - 1);
    r[ans[1] - ans[0]-1] = 0;
    return r;
}

中心扩散法
使用中心扩散法寻找回文数组,但是回文数组有两种可能,奇数个和偶数个。
设置两个指针left和right,分别位于对称中心的两边,向两边扩散,中心对称点是重复字符串则当成一个字符处理。


char * longestPalindrome(char * s){
    if(strlen(s)==0||strlen(s)==1) return s;
    int i,start,left,right,count,len;
    start = len =0;
    for(i=0;s[i]!='\0';i+=count){
        count = 1;
        left=i-1;
        right = i+1;
        while(s[right]!='\0'&&s[i]==s[right]){ //处理重复字符串
            right++;
            count++;
        }
        while(left>=0 && s[right]!='\0' && s[left] == s[right]){
            left--;
            right++;
        }
        if(right-left-1>len){
            start = left+1;
            len = right-left-1;
        }
    }
    s[start + len] = '\0';      // 原地修改返回
    return s + start;
}

在这里插入图片描述
在这里插入图片描述

char * longestPalindrome(char * s){
   if (strlen(s) < 2){
       return s;
   }

   int start  = 0,end  = 0;
   for (int  i =0 ;i< strlen(s);i++){
       int temp_s = i,temp_e = i;//中间对称型 abcba
       while(temp_s >=0 && temp_e < strlen(s) && s[temp_e] == s[temp_s]) {
           temp_s--;
           temp_e++;
       }
       temp_s++;// 最后一步不符合回退
       temp_e--;
       if(temp_e - temp_s > end - start){
           start = temp_s;
           end = temp_e;
       }

       if(i+1<strlen(s) && s[i] == s[i+1]) { //abba
        int temp_s = i,temp_e = i+1;
        while (temp_s >=0 && temp_e < strlen(s) && s[temp_e] == s[temp_s]) {
           temp_s--;
           temp_e++;
       }
       temp_s++;// 最后一步不符合回退
       temp_e--;
       if (temp_e - temp_s > end  - start) {
            start = temp_s;
            end = temp_e;
       }

    }

   }
   char *sub_s = malloc((end -start +1) * sizeof(char *));
   strncpy(sub_s, s+start, end - start +1);
   sub_s[end -start +1] = '\0';
   return sub_s;
}
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值