bzoj-3676 回文串
来源:互联网 发布:mac的excel数据有效性 编辑:程序博客网 时间:2024/04/28 08:36
题意:
给出一个长度为n的字符串,求它的某个回文子串长度乘出现次数的最大值;
n<=300000;
题解:
据说这题用回文自动机回文树之类的东西有一些更优的解法?
然而回文自动机似乎是在这题之后被引入OI的23333;
所以还有一些听起来比较靠谱的解法。。
我们先考虑求出所有的回文子串,由于一些原因这些本质不同的回文子串最多有O(n)个;
利用manacher算法算出每个回文子串的位置与长度之后,考虑如何计算一个字符串的出现次数;
一个子串是后缀的前缀,那么两个相同的子串在进行后缀排序之后必定相邻;
有了这个性质,我们就可以借助后缀数组的height数组,来快速找出height数组上连续一段大于等于该字符串长度的个数;
找这个可以用RMQ预处理+二分左侧右侧长度实现 (不要吐槽我又求了一遍LCP);
这个个数乘上字符串长度就是答案了,注意极限情况可能爆int;
而且因为这题的时间复杂度是O(后缀数组nlogn+RMQ预处理nlogn+Manacher二分nlogn)所以隐藏常数巨大!
因此我们可以加一个小优化,在二分的时候,特判一下左右第一个是否满足大于等于字符串长度,如果不满足就不二分了;
这样就可以通过了,然而速度真是太慢啦2333;
代码:
#include<stdio.h>#include<string.h>#include<algorithm>#define N 300010#define S 26using namespace std;typedef long long ll;int n,sa[N],rank[N],h[N],tr[N],hash[N];int f[N];int mi[N][20],LG[N];ll ans;char str[N];inline bool cmp(int x,int y,int len){if(x+len>=n)return 0;return rank[x]==rank[y]&&rank[x+len]==rank[y+len];}void getSA(){register int i;int k,cnt;for(i=0;i<n;i++)hash[str[i]-'a']++;for(i=0,cnt=-1;i<S;i++)if(hash[i])tr[i]=++cnt;for(i=1;i<S;i++)hash[i]+=hash[i-1];for(i=0;i<n;i++)rank[i]=tr[str[i]-'a'],sa[--hash[str[i]-'a']]=i;for(k=2;cnt<n-1;k<<=1){memset(hash,0,sizeof(int)*n);for(i=0;i<n;i++)hash[rank[i]]++;for(i=1;i<n;i++)hash[i]+=hash[i-1];for(i=n-1;i>=0;i--)if(sa[i]>=k>>1)tr[sa[i]-(k>>1)]=--hash[rank[sa[i]-(k>>1)]];for(i=1;i<=k>>1;i++)tr[n-i]=--hash[rank[n-i]];for(i=0;i<n;i++)sa[tr[i]]=i;for(i=1,cnt=0;i<n;i++)tr[sa[i]]=cmp(sa[i-1],sa[i],k>>1)?cnt:++cnt;memcpy(rank,tr,sizeof(int)*n);}for(i=0;i<n;i++){if(rank[i]){for(k=max(h[rank[i-1]]-1,1);k<n;k++)if(str[sa[rank[i]]+k-1]==str[sa[rank[i]-1]+k-1])h[rank[i]]=k;elsebreak;mi[rank[i]][0]=h[rank[i]];}}}inline int LCP(int x,int y){if(x==y)return n-x;if(rank[x]>rank[y])swap(x,y);int k=LG[rank[y]-rank[x]]; return min(mi[rank[x]+1][k],mi[rank[y]-(1<<k)+1][k]);}inline ll calc(int st,int len){int ret=1,l,r,mid;if(LCP(sa[rank[st]-1],st)>=len){l=1,r=rank[st];while(l<=r){mid=l+r>>1;if(LCP(sa[rank[st]-mid],st)>=len)l=mid+1;elser=mid-1;}ret+=r;}if(LCP(sa[rank[st]+1],st)>=len){l=1,r=n-rank[st]-1;while(l<=r){mid=l+r>>1;if(LCP(sa[rank[st]+mid],st)>=len)l=mid+1;elser=mid-1;}ret+=r;}return (ll)ret*len;}void manacher(){int i,ma,id;for(i=0,ma=-1,id=0;i<n;i++)//ÆæÊý{f[i]=max(0,min(f[id+id-i],ma-i+1));while(i+f[i]<n&&i-f[i]>=0&&str[i+f[i]]==str[i-f[i]]){f[i]++;if(i+f[i]-1>ma){ma=i+f[i]-1,id=i;ans=max(ans,calc(i-f[i]+1,f[i]+f[i]-1));}}}f[0]=0;for(i=0,ma=-1,id=0;i<n;i++)//żÊý{f[i]=max(0,min(f[id+id-i],ma-i+1));while(i+f[i]<n&&i-f[i]-1>=0&&str[i+f[i]]==str[i-f[i]-1]){f[i]++;if(i+f[i]>ma){ma=i+f[i],id=i;ans=max(ans,calc(i-f[i],f[i]+f[i]));}}}}int main(){int i,j,k;gets(str);n=strlen(str);getSA();LG[1]=0;for(i=2;i<=n;i++)LG[i]=LG[i>>1]+1;for(k=1;k<=19;k++)for(i=1;i<n-(1<<k-1);i++)mi[i][k]=min(mi[i][k-1],mi[i+(1<<k-1)][k-1]);manacher();printf("%lld\n",ans);return 0;}
0 0
- bzoj-3676 回文串
- BZOJ 3676 回文串
- BZOJ 3676 Apio2014 回文串 回文自动机
- 【BZOJ 3676】 [Apio2014]回文串 回文树
- BZOJ 3676 [Apio2014]回文串 回文自动机
- BZOJ 3676: [Apio2014]回文串 回文自动机
- [BZOJ]3676: [Apio2014]回文串 回文自动机
- [BZOJ]3676 [APIO2014] 回文串 回文自动机
- 【BZOJ 3676】 [Apio2014]回文串
- BZOJ 3676: [Apio2014]回文串
- BZOJ 3676: [Apio2014]回文串 回文串自动机
- bzoj 3676: [Apio2014]回文串 (回文自动机)
- [manacher 后缀自动机 || 回文自动机] BZOJ 3676 [Apio2014]回文串
- [BZOJ 3676][APIO 2014]回文串
- BZOJ 3676 & UOJ 103 [Apio2014]回文串
- BZOJ 3672([Apio2014]回文串-回文树)
- bzoj 3676: [Apio2014]回文串 manachar+后缀自动机+倍增(回文树)
- BZOJ 2565 最长双回文串(回文自动机)
- 文件输入和输出流
- HTML5中新表单元素及表单验证
- poj1155 TELE(树形dp+背包)
- 冒号用法
- java添加删除cookie
- bzoj-3676 回文串
- [Android] 关于Android的.so文件你所需要知道的 - 简书
- mongodb里的集合数据迁移到mysql库
- Same Tree
- 我家大门常打开——家用路由器安全探析
- window.location的相关用法
- ubuntu如何降级到之前的版本
- LeetCode---Next Permutation
- 15.target/action设计模式二