manacher算法专题

一、模板

算法解析:http://www.felix021.com/blog/read.php?2040

 1 *主要用来解决一个字符串中最长回文串的长度,在O(n)时间内,线性复杂度下,求出以每个字符串为中心的最长回文,奇数回文跟偶数回文一起考虑了
 2 S  $ # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
 3 P   1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
 4 
 5 最后所求的值就是max(P[i]-1)
 6 
 7 //输入,并处理得到字符串s,s[0]=$
 8 void getp()
 9 {
10     int p[1000], mx = 0, id = 0;
11     memset(p, 0, sizeof(p));
12     for (i = 1; s[i] != '\0'; i++) 
13     {
14         p[i] = mx>i ? min(p[2*id-i], mx-i) : 1;
15         while (s[i + p[i]] == s[i - p[i]]) p[i]++;
16         if (i + p[i] > mx) 
17         {
18             mx = i + p[i];
19             id = i;
20         }
21     }
22 }

 

二、题目

1、【HDU 4513】吉哥系列故事――完美队形II

题意:输入n(1 <= n <= 100000)个人的身高hi(50 <= hi <= 250),从这些人中连续挑出k个人,这k个人【身高是左右对称的,如果是k是奇数,那么中间那个人的身高任意】&&【从左到中间那个人,身高需保证不下降,如果用H表示新队形的高度,则H[1] <= H[2] <= H[3] .... <= H[mid]】,求k的最大值。

解题思路:一般的manacher添加的是’#’,但是本题左半边的身高不递减,所以添加的应该是(h[i]+h[i+1])/2,注意细节。处理后的第奇数个身高是添加上去的,第偶数个身高是一开始输入的,当i-p[i]是奇数时,无论hh[i-p[i]]hh[i+p[i]]是否相等,p[i]都应该+1

比如:h[] = 80 60 70 60 90 50 ==> hh[] = -1 65 80 70 60 65 70 65 60 75 90 70 50 65【有颜色的是原串】,以70为中心时,p[6]=4而不是3

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <string>
 6 using namespace std;
 7 const int N=2*1e5+10;
 8 int h[N], hh[N];
 9 int t, n;
10 int p[N], lenhh;
11 void getp()
12 {
13     int mx = 0, id = 0;
14     memset(p, 0, sizeof(p));
15     for (int i = 1; i<lenhh; i++) 
16     {
17         p[i] = mx>i ? min(p[2*id-i], mx-i) : 1;
18         while(hh[i + p[i]] == hh[i - p[i]] && hh[i-p[i]]<=hh[i-p[i]+1]) p[i]++;
19         if((i-p[i])%2) p[i]++;
20         if (i + p[i] > mx) 
21         {
22             mx = i + p[i];
23             id = i;
24         }
25     }
26 }
27 int main(){
28     scanf("%d", &t);
29     while(t--){
30         scanf("%d", &n);
31         for(int i=1; i<=n; i++){
32             scanf("%d", &h[i]);
33         }
34         h[0]=h[n], h[n+1]=h[1];
35         lenhh=2;
36         hh[1]=(h[0]+h[1])/2, hh[0]=-1;
37         for(int i=1; i<=n; i++){
38             hh[lenhh++]=h[i];
39             hh[lenhh++]=(h[i]+h[i+1])/2;
40         }
41 //        for(int i=0; i<lenhh; i++) cout<<hh[i]<<" ";
42 //        cout<<endl;
43         getp();
44         int maxn=0;
45 //        for(int i=0; i<lenhh; i++) cout<<p[i]<<" ";
46 //        cout<<endl;
47         for(int i=1; i<lenhh; i++) maxn = max(maxn, p[i]-1);
48         printf("%d\n", maxn);
49     }
50     return 0;
51 }
View Code

2、【HDU 3948The Number of Palindromes(manacher +建图 || )

题意:输入一个字符串(len<=1e5),求这个字符串中不同的回文子串的个数,如:【输入aaaa,有四个不同的回文子串,aaaaaaaaaa

解题思路:先用计算字符串的hash值,manacher算法求出p[],枚举每一个中心点,从可能的最长回文串的两端逐渐向里判断是否出现过,如果长的出现过了,那短的也一定出现过。判断这个字符串是否出现过目前一共有两种方法:【最后的点的总数不会超过3*1e5的,那就将每一个hash值都对mod(mod=3*1e5+10)取余,建图,hash%mod后相等的利用head连成一个图&&【这一种方式就是比较费时但也比较直接的,直接用map判断这个hash值是不是出现过】

Get新技能:将判断字符串是否出现过转化为判断hash值是不是出现过,同时建图判断hash值是否出现过。

manacher+建图

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <string>
  6 #include <map>
  7 using namespace std;
  8 
  9 #define ull unsigned long long
 10 #define ll __int64
 11  
 12 const int N = 1e5+10;
 13 const ull bas = 311;
 14 ull has[N], base[N];
 15 char s[N], ss[2*N];
 16 int lenss, p[2*N];
 17 
 18 struct hash_map{
 19     const static int mod=3*N+10;//一个静态的、值不能被改变的整型常量
 20     int idx, head[mod];
 21     
 22     struct hash_tables{
 23         int next;
 24         ull key;
 25     }ele[2*N];
 26     
 27     void init(){
 28         idx = 0;
 29         memset(head, 0xff, sizeof(head));//初始化为-1 
 30     }
 31     
 32     void clear(){// clear的效率要高于init的效率,后期的用以替换init 
 33         for(int i=0; i<idx; i++){
 34             head[ele[i].key%mod] = -1;
 35         }
 36         idx = 0;
 37     }
 38     
 39     void insert(ull x){
 40         int tmp = x%mod;
 41         ele[idx].key = x;
 42         ele[idx].next = head[tmp];
 43         head[tmp] = idx++;
 44     }
 45     
 46     bool find(ull x){
 47         int hashcode = x%mod;
 48         for(int i=head[hashcode]; i!=-1; i=ele[i].next){
 49             if(ele[i].key == x) return true;
 50         }
 51         return false;
 52     }
 53 }hashi; // 将hash表的实现封装成一个类 
 54 
 55 ll ans;
 56 
 57 void manacher(){
 58     int mx = 0, id = 0;
 59     memset(p, 0, sizeof(p));
 60     for (int i = 1; i<lenss; i++) 
 61     {
 62         p[i] = mx>i ? min(p[2*id-i], mx-i) : 1;
 63         while (ss[i + p[i]] == ss[i - p[i]]) p[i]++;
 64         if (i + p[i] > mx) 
 65         {
 66             mx = i + p[i];
 67             id = i;
 68         }
 69     }
 70 }
 71 void init(){
 72     ss[0] = '$', ss[1] = '#';
 73     lenss = 2, has[0]=0;
 74     int len=strlen(s);
 75     for(int i=0; i<len; i++){
 76         has[i+1] = has[i]*bas+s[i]-'a'+1;
 77         ss[lenss++] = s[i];
 78         ss[lenss++] = '#';
 79     }
 80     ss[lenss]='\0';
 81     manacher();
 82 }
 83 int begn, ed;
 84 void find(int id, int dis){
 85     begn=id-dis+1, ed=id+dis-1;
 86     if(begn%2==1) begn++, ed--;
 87     while(begn<=ed){
 88         ull k = has[ed/2] - has[begn/2-1]*base[ed/2-begn/2+1];
 89         if(!hashi.find(k)){
 90             hashi.insert(k);
 91             ans++;
 92         }
 93         else break;
 94         begn+=2, ed-=2;
 95     }
 96 }
 97 int main(){
 98     int t, cas=1;
 99     scanf("%d", &t);
100     hashi.init();
101     base[0] = 1;
102     for(int i=1; i<N; i++) base[i] = base[i-1]*bas;
103     
104     while(t--){
105         hashi.clear();
106         scanf("%s", s);
107         init();
108         
109         ans = 0;
110         for(int i=1; i<lenss; i++){
111             find(i, p[i]);
112         }
113         printf("Case #%d: %I64d\n", cas++, ans);
114     }
115     return 0;
116 }
View Code

manacher+map

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <string>
 6 #include <map>
 7 using namespace std;
 8 
 9 #define ull unsigned long long
10  
11 const int N = 1e5+10;
12 const ull bas = 311;
13 ull has[N], base[N];
14 char s[N], ss[2*N];
15 int lenss, p[2*N];
16 map<ull, int>mp;
17 
18 int ans;
19 
20 void manacher(){
21     int mx = 0, id = 0;
22     memset(p, 0, sizeof(p));
23     for (int i = 1; ss[i] != '\0'; i++) 
24     {
25         p[i] = mx>i ? min(p[2*id-i], mx-i) : 1;
26         while (ss[i + p[i]] == ss[i - p[i]]) p[i]++;
27         if (i + p[i] > mx) 
28         {
29             mx = i + p[i];
30             id = i;
31         }
32     }
33 }
34 void init(){
35     ss[0] = '$', ss[1] = '#';
36     lenss = 2, has[0]=0;
37     int len =  strlen(s);
38     for(int i=0; i<len; i++){
39         has[i+1] = has[i]*bas+s[i]-'a'+1;
40         ss[lenss++] = s[i];
41         ss[lenss++] = '#';
42     }
43     ss[lenss] = '\0';
44     manacher();
45 }
46 int begn, ed;
47 void find(int id, int dis){
48     begn=id-dis+1, ed=id+dis-1;
49     if(begn%2==1) begn++, ed--;
50     while(begn<=ed){
51         ull k = has[ed/2] - has[begn/2-1]*base[ed/2-begn/2+1];
52         if(!mp[k]){
53             mp[k] = 1;
54             ans++;
55         }
56         else break;
57         begn+=2, ed-=2;
58     }
59 }
60 int main(){
61     int t, cas=1;
62     scanf("%d", &t);
63     
64     base[0] = 1;
65     for(int i=1; i<N; i++) base[i] = base[i-1]*bas;
66     
67     while(t--){
68         scanf("%s", s);
69         init();
70         
71         ans = 0, mp.clear();
72         for(int i=1; i<lenss; i++){
73             find(i, p[i]);
74         } 
75         printf("Case #%d: %d\n", cas++, ans);
76     }
77     return 0;
78 }
View Code

 

posted @ 2016-05-07 15:31  穿破石  阅读(358)  评论(0编辑  收藏  举报