BZOJ 3676[Apio2014]回文串
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3676[Apio2014]回文串相关的知识,希望对你有一定的参考价值。
【链接】h在这里写链接
【题意】
给你一个字符串s.
定义一个子串的出现值为它出现的次数*字符串的长度。
让你求里面的回文子串的最大出现值
|s|<=3e5
定义一个子串的出现值为它出现的次数*字符串的长度。
让你求里面的回文子串的最大出现值
|s|<=3e5
【题解】
马拉车算法里面.
只有在回文往外扩展超过mx的时候,才出现了不同的回文。
只有这个时候需要计算。
快速计算一个子串在字符串中出现了多少次。
可以用二分。
比如子串为s[x..y]
则先获取temp = Rank[x];
然后看看往左height>=y-x+1的能走多远。
再看看往右height>=y-x+1的有多少。
用RMQ看看height在temp,mid(二分的中点)之间的height最小值是多少
显然有单调性.
找到最左和最右,就是出现的次数了。
马拉车算法虽然改造了原串。
但可以记录每个位置原本是属于字符串的哪一个位置的。
只有在回文往外扩展超过mx的时候,才出现了不同的回文。
只有这个时候需要计算。
快速计算一个子串在字符串中出现了多少次。
可以用二分。
比如子串为s[x..y]
则先获取temp = Rank[x];
然后看看往左height>=y-x+1的能走多远。
再看看往右height>=y-x+1的有多少。
用RMQ看看height在temp,mid(二分的中点)之间的height最小值是多少
显然有单调性.
找到最左和最右,就是出现的次数了。
马拉车算法虽然改造了原串。
但可以记录每个位置原本是属于字符串的哪一个位置的。
【错的次数】
0
【反思】
在这了写反思
【代码】
#include<bits/stdc++.h> using namespace std; const int N = 3e5; const int MAX_CHAR = 255;//每个数字的最大值。 char s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存 int Sa[N + 10], T1[N + 10], T2[N + 10], C[N+10]; int Height[N + 10], Rank[N + 10]; void build_Sa(int n, int m) { int i, *x = T1, *y = T2; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[i] = s[i]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i; for (int k = 1; k <= n; k <<= 1) { int p = 0; for (i = n - k; i<n; i++) y[p++] = i; for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[y[i]]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[Sa[0]] = 0; for (i = 1; i<n; i++) x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++; if (p >= n) break; m = p; } } void getHeight(int n) { int i, j, k = 0; for (i = 1; i <= n; i++) Rank[Sa[i]] = i; for (i = 0; i<n; i++) { if (k) k--; j = Sa[Rank[i] - 1]; while (s[i + k] == s[j + k]) k++; Height[Rank[i]] = k; } } const int MAXL = 19;//log2数组的最大长度 const int INF = 0x3f3f3f3f;//数值绝对值的最大值 struct abc{ int pre2[MAXL+5],need[N+10]; int fmin[N+10][MAXL+5]; void init(int n) { pre2[0] = 1; for (int i = 1;i <= MAXL;i++) { pre2[i] = pre2[i-1]<<1; } need[1] = 0; need[2] = 1; int temp = 2; for (int i = 3; i <= n; i++)//need[i]表示长度为i是2的多少次方,可以理解为[log2i] if (pre2[temp] == i) need[i] = need[i - 1] + 1, temp++; else need[i] = need[i - 1]; } void getst(int *a,int n) { memset(fmin,INF,sizeof fmin); for (int i = 1;i <= n;i++)//下标从0开始就改成对应的就好 fmin[i][0] = a[i]; for (int l = 1;pre2[l]<=n;l++) for (int i = 1;i <= n;i++) if (i+pre2[l]-1<=n) fmin[i][l] = min(fmin[i][l-1],fmin[i+pre2[l-1]][l-1]); } int getmin(int l,int r) { int len = need[r-l+1]; return min(fmin[l][len],fmin[r-pre2[len]+1][len]); } }ST; char S[2*N+100]; int belong[2*N+100],p[2*N+100]; int n; long long ans = 0; int lcp(int x,int y) { return ST.getmin(x+1,y); } void cal(int x,int y) { int len = y - x + 1;//子串s[x..y]的长度 int temp = Rank[x];//这个后缀x的排名 int pr = temp;//最右能到哪里 int l = temp+1,r = n; while (l <= r) { int mid = (l+r)>>1; if (lcp(temp,mid)>=len) { pr = mid; l = mid + 1; }else r = mid - 1; } int pl = temp; l = 1,r = temp - 1; while (l <= r) { int mid = (l+r)>>1; if (lcp(mid,temp)>=len) { pl = mid; r = mid - 1; }else l = mid + 1; } ans = max(ans,1LL*(pr-pl+1)*len); } int main() { //freopen("F:\\rush.txt","r",stdin); scanf("%s", s); n = strlen(s); s[n] = 0; build_Sa(n + 1, MAX_CHAR);//注意调用n+1 getHeight(n); ST.init(n); ST.getst(Height,n); int len = 0; S[len++] = ‘$‘; S[len++] = ‘#‘; for (int i = 0;i < n;i++) { S[len++] = s[i]; belong[len-1] = i; S[len++] = ‘#‘; } int mx = 0,id = 0; for (int i = 1;i < len;i++) { if (i > mx) p[i] = 1; else p[i] = min(p[2*id-i],mx-i+1); while (S[i-p[i]]==S[i+p[i]]) { if (i+p[i] > mx && S[i-p[i]+1]!=‘#‘) { cal(belong[i-p[i]+1],belong[i+p[i]-1]); } p[i]++; } if (i + p[i]-1 > mx) mx = i + p[i] - 1,id = i; } printf("%lld\n",ans); return 0; }
以上是关于BZOJ 3676[Apio2014]回文串的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ3676][APIO2014]回文串(Manacher+SAM)