最长回文子串

说明:转载请注明出处

 

题目:
给定一个字符串,求它的最长回文子串的长度。

 

思路一:采用中间法比较

/*说明:中心法求最长回文子串
  时间复杂度为O(n^2)时间,空间复杂度O(1)
  思路:如果一段字符串是回文,那么以某个字符为中心的前缀和后缀都是相同的
  反之则肯定不是回文字符串,不过要注意区分奇偶
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*以low和high为中心找到最长回文字符串长度*/
int centerPalindrome(char*S, int N, int low, int high)
{
    int max = 0;
    /*需要单独处理,返回正确长度*/
    if (low == high)
    {
        max = 1;
        low--;
        high++;
    }
    while (low>=0 && high<N)
    {
        if (S[low] != S[high])
        {
            break;
        }
        low--;
        high++;
        max += 2;
    }
    
    return (max==0)?1:max;
}
/*主程序,通过中间开始判断获取最长回文字符串*/
char* longestPalindrome(char* S, int N)
{
    int i;
    int j = 0;
    int mid = 0;
    int maxLen = 0;
    int max = 0;
    char *str = NULL;
    int start;
    int end;
    
    for (i=0; *(S+i)!='\0'; i++)
    {
         /*奇序列*/
          max = centerPalindrome(S, N, i, i);
          if(maxLen < max)
          {
            maxLen = max;
            mid = i;
          }
          /*偶序列*/
          max = centerPalindrome(S, N, i, i+1);
          if(maxLen < max)
          {
            maxLen = max;
            mid = i;
          }
    }
    
    /*分配内存将最大回文字符串返回*/
    str = (char *)malloc(maxLen+1);
    start =  mid-(maxLen-1)/2;
    end = start +  maxLen;
    for (i=start; i<end; i++)
    {
        str[i-start] = *(S+i);
    }
    str[maxLen] = '\0';
    return str;
}
int main()
{
    char s[] = "123321";
    printf("%s\n", longestPalindrome(s, strlen(s)));
    return 0;
}


思路二:采用动态规划求解

 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/*采用动态规划求解,考虑要求最大回文字符串,如
  "aba"已经是回文字符串,则"xabax"必然也是回文字符串
  那思路有了,实现考虑用数组记录任意i到j是否为回文字符串
  table[i][i]肯定为1,那table[i][j]=table[i+1][j-1] && s[i]=s[j]
  即如果从i到j是回文则s[i]和s[j]必然应该相等,且从i+1到j-1必然是回文
*/
char* longestPalindrome(char* S, int N)
{
    int i;
    int j;
    int mid = 0;
    int maxLen = 1;
    int len;
    bool table[100][100] = {false};
    char* str = NULL;
    
    /*初始化table[i][i]为TRUE*/
    for (i=0; i<N; i++)
    {
       table[i][i] = true;
       /*初始化table[i][i+1]*/
       if (i<N-1 && S[i]==S[i+1])
       {
           if (maxLen == 0)
           {
                maxLen = 2;
                mid = i;
           }
           table[i][i+1] = true;
       }
    }
    
    for (len=2; len<=N; len++) /*回文字符串总长度*/
    {
        for (i=0; i<N-len+1; i++) /*回文字符串开始的位置*/
        {
            j = i+len-1; /*回文字符串结束的位置*/
            if (S[i]==S[j] && table[i+1][j-1])
            {
                table[i][j] = true;
                if (maxLen < len)
                {
                    maxLen = len;
                    mid = i;
                }
            }
        }
    }
    
    str = (char*)malloc(maxLen+1);
    j = 0;
    for (i=mid; i<mid+maxLen; i++)
    {
        str[j++] = *(S+i);
    }
    str[j] = '\0';
    return str;
}
int main()
{
    char s[] = "012332";
    char* str = longestPalindrome(s, strlen(s));
    
    printf("str %s\n", str);
    free(str);
    str = NULL;
    return 0;
}


 

思路三:Manacher算法,且这个算法求最长回文子串的时间复杂度是线性O(N)的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MIN(X, Y) ((X)<(Y)?(X):(Y))
/*说明:manacher可以将找最大回文字符串的时间复杂度降低为O(n)
  具体文章可以参考(我觉得比较好理解的文章):
  http://blog.csdn.net/runninghui/article/details/39322649
  http://blog.csdn.net/synapse7/article/details/18908413
*/
 
/*将字符串S加工,如S="abc", 则T="$#a#b#c#"*/
char* preProcess(char *S, int N)
{
    char* T = NULL;
    int i;
    int j = 0;
    
    T = (char*)malloc((N+1)*2);
    /*注意:不能直接用T++往前移动,返回的时候T已经指向字符串末端,这样有问题*/
    T[j++] = '$';
    T[j++] = '#';
    
    for (i=0; *(S+i)!='\0'; i++)
    {
        T[j++] = *(S+i);
        T[j++] = '#';
    }
    T[j] = '\0';
    
    return T;
}
/*找最大回文字符串主程序*/
char* manacher(char* S, int N)
{
   int id = 0;/*记录i之前最大回文子串中心的位置*/
   int *p = NULL; /*P[i]记录以字符S[i]为中心的最长回文子串长度*/
   int mx = 0;/*记录id+p[i]的值*/
   char *str = NULL;
   char *T;
   int i;
   int mid = 0;
   int maxLen = 0;
   int start;
   int end;
   
   T = preProcess(S, N);
   p = (int *)malloc(sizeof(int)*(strlen(T)));
   memset(p, 0, sizeof(int)*(strlen(T)));
   for (i=1; *(T+i)!='\0'; i++)
   {
        /*如果mx比i大,则p[i]至少比mx-i或者p[j]要大,其中j为i关于id的对称点*/
        if (mx > i)
        {
            p[i] = MIN(mx-i, p[2*id-i]);
        }
        /*否则认为没有覆盖到i值,为默认值*/
        else
        {
            p[i] = 1;
        }
        
        /*以i为中心往两边扩展,更新p[i]的值*/
        while (*(T+i-p[i]) == *(T+i+p[i]))
        {
            p[i]++;
        }
        /*如果以i为中心的回文字串更大则更新id的值*/
        if (i+p[i] > mx)
        {
            id = i;
            mx = i+p[i];
        }
        
        /*注意此处需要重新比较,上面比较和此处有区别*/
        if (p[i] > maxLen)
        {
            mid = i;
            maxLen = p[i];
        }
   }
   
   str = (char *)malloc(maxLen);
   start = (mid-maxLen)/2;
   end = start+maxLen;
   for (i=start; i<end; i++)
   {
        str[i-start] = *(S+i);
   }
   str[maxLen-1] = '\0';
   free(T);
   T = NULL;
   return str;
}
int main()
{
    char s[] = "132321abc";
    
    char *t = manacher(s, strlen(s));
    
    printf("result %s\n", t);
    free(t);
    t = NULL;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值