SCL--Manacher(线性回文子串处理算法)
2015-07-21 10:48:31
【学习传送门】
总结:线性求以每个位置为中心的最长回文串O(n)算法 get 。
(1)在相邻位置间插入特殊字符 / 数字。从而将所有回文串的长度转化为奇数。
(2)用类似DP的方法线性求出每个点的 P[] 函数,P[i]
void Manacher(){ int top = 2 * n + 1; int tmax = 0,id = 0; for(int i = 1; i <= top; ++i){ if(tmax > i){ P[i] = min(P[2 * id - i],tmax - i); } else{ P[i] = 1; } while(tc[i - P[i]] == tc[i + P[i]]){ P[i]++; } if(i + P[i] > tmax){ tmax = i + P[i]; id = i; } } }
经典题目:
(1)HDU 3068 求最长回文串(如果字符串从 1 位置开始的话,注意把 str[0] 赋值为另一个特殊字符,比如 ‘?’。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 110010 * 2; char s[MAXN],ts[MAXN]; int P[MAXN]; int main(){ while(scanf("%s",ts + 1) != EOF){ int len = strlen(ts + 1); for(int i = 1; i <= len; ++i){ s[i * 2 - 1] = '#'; s[i * 2] = ts[i]; } s[0] = '?'; s[len * 2 + 1] = '#'; s[len * 2 + 2] = '\0'; int tmax = 0,id = 0; len = len * 2 + 1; int ans = 0; for(int i = 1; i <= len; ++i){ if(tmax > i){ P[i] = min(P[2 * id - i],tmax - i); } else{ P[i] = 1; } while(s[i - P[i]] == s[i + P[i]]){ P[i]++; } if(i + P[i] > tmax){ tmax = i + P[i]; id = i; } ans = max(ans,P[i]); } printf("%d\n",ans - 1); } return 0; }
(2)2015 计蒜之道初赛(第三场)第二题的中等难度。
题意:给出 n (n <= 20000)个数,m(m <= 3000)个询问,每次求出询问区间内的所有回文串内的数字和。
思路:先用 manacher 预处理,然后对于每个询问暴力扫描每个区间内点的 P 函数,且将其最长回文限制在询问区间内,然后就是一个统计的问题了。发现越靠近回文串对称中心的数字需要统计的次数越多,这就需要统计前缀和的前缀和。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 40010; int n,m; int tc[MAXN],P[MAXN]; ll sum[MAXN],sum2[MAXN]; void Manacher(){ int top = 2 * n + 1; int tmax = 0,id = 0; for(int i = 1; i <= top; ++i){ if(tmax > i){ P[i] = min(P[2 * id - i],tmax - i); } else{ P[i] = 1; } while(tc[i - P[i]] == tc[i + P[i]]){ P[i]++; } if(i + P[i] > tmax){ tmax = i + P[i]; id = i; } } } int main(){ int a,b; scanf("%d%d",&n,&m); for(int i = 1; i <= n; ++i){ scanf("%d",&a); tc[i * 2 - 1] = -200; tc[i * 2] = a; } tc[0] = -300; tc[n * 2 + 1] = -200; Manacher(); int top = 2 * n + 1; for(int i = 1; i <= top; ++i){ sum[i] = sum[i - 1]; if(tc[i] >= -100){ sum[i] += tc[i]; } } for(int i = 1; i <= top; ++i){ sum2[i] = sum2[i - 1]; if(tc[i] >= -100){ sum2[i] += sum[i]; } } /*for(int i = 1; i <= top; ++i){ printf("P[%d] : %d\n",i,P[i]); } for(int i = 1; i <= top; ++i){ printf("sum[%d] : %lld\n",i,sum[i]); } for(int i = 1; i <= top; ++i){ printf("sum2[%d] : %lld\n",i,sum2[i]); }*/ for(int o = 1; o <= m; ++o){ scanf("%d%d",&a,&b); a *= 2; b *= 2; ll ans = 0; for(int i = a; i <= b; ++i){ int minr = min(P[i],min(i - a + 1,b - i + 1)); int pos = i + minr - 1; if(pos & 1) pos--; //printf("i : %d , minr : %d , pos : %d",i,minr,pos); ll cur; if(i & 1){ cur = 2LL * (sum2[pos] - sum2[i] - sum[i] * ((pos-i+1) / 2LL)); } else{ cur = 2LL * (sum2[pos] - sum2[i] - sum[i] * ((pos - i) / 2LL)); } if(!(i & 1)) cur += (ll)tc[i] * ((pos - i) / 2 + 1); //printf(" %lld\n",cur); ans += cur; } printf("%lld\n",ans); } return 0; }