leetCode & hihocoder:最长回文子字符串时间复杂度为O(N)解法

转载之http://blog.csdn.net/ggggiqnypgjg/article/details/6645824地址


之前自己做过这道题目,不过算法复杂度是O(n^2)的时间复杂度和空间复杂度的

后来http://hihocoder.com/contest/hiho1/rank 这个有比赛,写了个O(n^2)的

一致TLE,看到discuss才发现有O(N)算法,真的是膜拜 

代码来源   http://acm.hust.edu.cn:8080/judge/problem/viewSource.action?id=140283

[cpp]  view plain copy
  1. /***************************************** 
  2. * 
  3. * 2011,UESTC_ACM 
  4. * 回文串 
  5. * By a180285 
  6. * O(n) 算法  
  7. *****************************************/  
  8.   
  9. # include <math.h>  
  10. # include <stdio.h>  
  11. # include <string.h>  
  12. # include <stdlib.h>  
  13. # include <algorithm>  
  14. # include <iostream>  
  15. # include <string>  
  16. # include <queue>  
  17. # include <stack>  
  18. # include <map>  
  19. # include <set>  
  20. # include <vector>  
  21. # include <cstring>  
  22. # include <list>  
  23. # include <ctime>  
  24. # include <sstream>  
  25.   
  26. # define For(i,a)   for((i)=0;i<(a);(i)++)  
  27. # define MAX(x,y)   ((x)>(y)? (x):(y))  
  28. # define MIN(x,y)   ((x)<(y)? (x):(y))  
  29. # define sz(a)      (sizeof(a))  
  30. # define MEM(a)     (memset((a),0,sizeof(a)))  
  31. # define MEME(a)    (memset((a),-1,sizeof(a)))  
  32. # define MEMX(a)    (memset((a),0x7f,sizeof(a)))  
  33. # define pb(a)      push_back(a)  
  34.   
  35. using namespace std;  
  36.   
  37. typedef long long           ll      ;  
  38. typedef unsigned long long  ull     ;  
  39. typedef unsigned int        uint    ;  
  40. typedef unsigned char       uchar   ;  
  41.   
  42.   
  43. template<class T> inline void checkmin(T &a,T b){if(a>b) a=b;}  
  44. template<class T> inline void checkmax(T &a,T b){if(a<b) a=b;}  
  45.   
  46. const int oo=1<<30          ;  
  47. const double eps=1e-7       ;  
  48. const int N=1               ;  
  49. const int M=1100110*2               ;  
  50. const ll P=10000000097ll    ;  
  51.   
  52. char str[M];//start from index 1  
  53. int p[M];  
  54. char s[M];  
  55. int n;  
  56.   
  57. void kp()  
  58. {  
  59.     int i;  
  60.     int mx = 0;  
  61.     int id;  
  62.     for(i=1; i<n; i++)  
  63.     {  
  64.         if( mx > i )  
  65.             p[i] = MIN( p[2*id-i], p[id]+id-i );  
  66.         else  
  67.             p[i] = 1;  
  68.         for(; str[i+p[i]] == str[i-p[i]]; p[i]++)  
  69.             ;  
  70.         if( p[i] + i > mx )  
  71.         {  
  72.             mx = p[i] + i;  
  73.             id = i;  
  74.         }  
  75.     }  
  76. }  
  77.   
  78. void pre()  
  79. {  
  80.     int i,j,k;  
  81.     n = strlen(s);  
  82.     str[0] = '$';  
  83.     str[1] = '#';  
  84.     For(i, n)  
  85.     {  
  86.         str[i*2 + 2] = s[i];  
  87.         str[i*2 + 3] = '#';  
  88.     }  
  89.     n = n*2 + 2;  
  90.     str[n] = 0;  
  91. }  
  92.   
  93. void pt()  
  94. {  
  95.     int i;  
  96.     int ans = 0;  
  97. //    For(i, n)  
  98. //        printf("%c", str[i]);  
  99. //    puts("");  
  100. //    For(i, n)  
  101. //        printf("%d", p[i]);  
  102. //    puts("");  
  103.   
  104.     For(i, n)  
  105.         checkmax(ans, p[i]);  
  106.     printf("%d\n", ans-1);  
  107. }  
  108.   
  109. int main()  
  110. {  
  111.     int n = 0;  
  112.     cin>> n;  
  113.     while( n--){  
  114.         cin>>s;  
  115.         pre();  
  116.         kp();  
  117.         pt();  
  118.     }  
  119.     return 0;  
  120. }  

具体做法可以参考链接;

对于一个字符串数组,我们将其插入一些字符,例如“#”

例如字符     a b c d d f g g

插入后      #a#b#c#d#d#f#g#g#

p[id]       12121212

定义数组 p[],对于插入后的字符串数组我们为chs[]吧

p[id] 表示位置id上为中心的最长回文串向右边可以扩展p[id]个位置,显然,可知

p[id] - 1 就是以id为中心的最长回文串的长度了

定义一个id,当前所知道能最右边扩展的位置的中心点;mx,该id

可以向右扩展mx位

那么只要求出p[] 这个数组,就可以知道最长回文串的长度了

 

上面的式子是根据回文串的对称性做的,理解上不难

因为是从低坐标向高坐标遍历的,id总是小于等于i

对于 p[i], 如果当前mx的值是大于i,所以i是位于以id为中心的回文串中的

也就是说以id为中心,可以找到i的对称,称其为j,(j 总是小于i)

因为p[j]是已知的,只需求出j 的值即可 j = 2 * id - i,这是第一种情况

第二种情况是在当mx - i 也就说 i在以id为中心的回文串中的长度,小于p[j]的长度,看上图,第二行的

右边的长方体的长度是大于mx的,(左右两边是对称的),由于mx之外值我们还没有比较,所以

当前只能取两者中较小的。


算法的构思十分巧妙,再次膜拜


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值