转载于:http://www.ituring.com.cn/article/58558

/*回文分割
原题对一个字符串按照回文进行分割,例如aba|b|bbabb|a|b|aba就是字符串ababbbabbababa的一个回文分割,
每一个字串都是一个回文。请找到可以分割的最少的字串数。例如:
1. ababbbabbababa最少4个字符串,分割三次:a|babbbab|b|ababa
2. 如果字符串整体是回文,则需要0次分割,最少1个字符串*/
/*
思想:先不要理会最少分割,从对字符串进行回文分割入手,对于回文字符串的判断是不可避免的。
可以从字符串的第一个开始,找到满足回文的字串。假设str[0..k]是一个回文字串(包括0和k)
问题就可以分解为子问题:假设cout[i]表示从i开始,包括i的字符串的回文分割数(当然是有很多可能)
如果str[0..k]是一个回文串,则count[i] =count[k+1] +1.找到递归表达式,这里要主要,从0开始,可能
会有多个回文字串,也正是因为如此,才有最少分割一说
*/
/*
设定P[i][j]:
当str[i..j]是回文时,为true
当str[i..j]不是回文,为false
所以有
i==j            p[i][j] =true;
j == i+1    p[i][j] =true;
其他,   p[i][j] = p[i +1][j-1] && (str[i] == str[j])
*/
//这样就有了动态规划的实现,根据状态转移的方程,p[i][j]所代表的字符串,长度从1开始变换,逐渐到整个字符串
//所以为循环应该是要判断字串的长度
#define TRUE   1
#define FALSE  0

int **DP;
void SplitPalindrome(char* str )
{
 int L,strlength,i,j;
 
 strlength = strlen(str);
 //下面的是初始化,节省空间的
 DP =(int **)malloc(strlength * sizeof(int*));
 for(i=0 ;i <strlength;i++)
 {
  DP[i] = (int*)malloc(strlength * sizeof(int));
 }
 for(i = 0;i < strlength;i++)
  for(j = 0; j < strlength ;j++)
  {
   if(i != j)
    DP[i][j] = FALSE;
   else
    DP[i][j] = TRUE;
  }
 //长度为从2开始
 for(L = 2 ; L <= strlength; L++)
  for(i =0 ;i < strlength - L + 1; i++)
  {
   j = i + L - 1; 
   if(L == 2)
    DP[i][j] = (str[i] == str[j]);
   else
    DP[i][j] = DP[i +1][j - 1] && (str[i] == str[j]);
  }
 //打印结果
 for(i = 0;i < strlength;i++)
 {
  for(j =0 ; j < strlength ;j++)
  {
   printf("%d ",DP[i][j]);
  }
  printf("\n");
 }
  
 return;
}
/*
给定字符串,找到它的最长回文子串,都有哪些思路呢?例如"adaiziguizhongrenenrgnohziugiziadb",
回文字串很多了,但最长的是"daiziguizhongrenenrgnohziugiziad"。
第一个方法当然是暴力法,外面的两层循环找到所有子串,第三层循环判断子串是否是回文。方法的时间复杂度为O(n^3),空间复杂度为O(1)。
第二个方法,大家是否记得上期的题目中,是如何判断子串是回文的么?采用的是动态规划的方法。
开辟一个P[i][j]用来表示str[i..j]是否为回文,P[i][j]的状态转移方程如下:
1. 当i==j时,P[i][j]=true
2. 当i+1==j时,P[i][j]=str[i]==str[j]
3. 其他,P[i][j]=P[i+1][j-1]&&(str[i]==str[j])
求出P之后,直接循环判断出 p[i][j] = true时的 j - i 的最大即可
这样,这个方法的时间复杂度为O(n^2),空间复杂度为O(n^2)。比暴力法有很大的改进。
第三个方法,可以从上面那个方法的状态转移方程获得启发,对于每一个回文子串可以先确定一个中心,
然后向两边扩展,这样可以在时间复杂度O(n^2),空间复杂度O(1)的情况下完成,需要注意的是,
长度为奇数和偶数的中心的情况是不同的。示例代码如下
*/
int longest = 1;
int start = 0;
void longestPalindrome(char *str)
{
 int i,j;
 int strlength;
 int low,high;
 
 strlength = strlen(str);
 for(i = 0; i< strlength;i++)
 {
  //先判断以i为中心的奇数情况
  low  = i - 1;
  high = i + 1;
  while(low >=0 && high < strlength &&(str[low] == str[high]))
  {
   if(longest <high - low + 1)
   {
    longest = high - low + 1;
    start = low;
   }
   low --;
   high ++;
  }
   //先判断以i为中心的偶数情况
  low  = i ;
  high = i + 1;
  while(low >=0 && high < strlength &&(str[low] == str[high]))
  {
   
   if(longest <high - low + 1)
   {
    longest = high - low + 1;
    start = low;
   }
   low --;
   high ++;
  }
 }
 
}
/*
第五个方法叫做Manacher算法,是一种线性时间的方法,非常巧妙。首先,我们在上面的方法中个,都要考虑回文长度为奇数或者偶数的情况。
这个 方法,引入一个技巧,使得奇数和偶数的情况统一处理了。具体做法如下:

基本转自博客:http://www.felix021.com/blog/read.php?2040 博客中对Manacher算法介绍得也非常好,向大家推荐。
abba转换为#a#b#b#a#,也就是在每一个字符两边都加上一个特殊字符。*/

void main()
{
 char *s ="ababbbabbababa";
 longestPalindrome(s);
  printf("The begin index %d of longest Palindrome %d",start,longest);
}