字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串
Description
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。
Input
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
Output
输出一个整数,为逝查回文子串的最大出现值。
Sample Input
abacaba
【样例输入2]
www
Sample Output
7
【样例输出2]
4
HINT
一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。
在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:
● a出现4次,其出现值为4:1:1=4
● b出现2次,其出现值为2:1:1=2
● c出现1次,其出现值为l:1:l=l
● aba出现2次,其出现值为2:1:3=6
● aca出现1次,其出现值为1=1:3=3
●bacab出现1次,其出现值为1:1:5=5
● abacaba出现1次,其出现值为1:1:7=7
故最大回文子串出现值为7。
【数据规模与评分】
数据满足1≤字符串长度≤300000。
这道题要求出每一个回文串,以及出现的次数。
先用马拉车算法求出每个位置最长的回文子串,可以证明:从每个位置上取出最长的回文串去枚举一定能枚举出最优解。
接着对于,每个位置的最长回文子串用SA与ST快速求出其出现的次数,然而这并非正解,回文自动机才是(话说APIO2014的时候还没有这玩意儿),那时这个算法应该是最优的了。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=;
char s[maxn],S[maxn<<];
int maxl[maxn<<];
int pos[maxn<<],maxL[maxn];
int rank[maxn],Wa[maxn],Wb[maxn],Wv[maxn],Ws[maxn],sa[maxn],r[maxn];
int lcp[maxn],len;
int mm[maxn],Min[maxn][];
bool cmp(int *p,int a,int b,int l){
return p[a]==p[b]&&p[a+l]==p[b+l];
} void DA(int n,int m){
int i,j,p,*x=Wa,*y=Wb,*t;
for(i=;i<m;i++)Ws[i]=;
for(i=;i<n;i++)++Ws[x[i]=r[i]];
for(i=;i<m;i++)Ws[i]+=Ws[i-];
for(i=n-;i>=;i--)sa[--Ws[x[i]]]=i; for(j=,p=;p<n;j<<=,m=p){
for(p=,i=n-j;i<n;i++)y[p++]=i;
for(i=;i<n;i++)
if(sa[i]>=j)
y[p++]=sa[i]-j; for(i=;i<m;i++)Ws[i]=;
for(i=;i<n;i++)++Ws[Wv[i]=x[y[i]]];
for(i=;i<m;i++)Ws[i]+=Ws[i-];
for(i=n-;i>=;i--)sa[--Ws[Wv[i]]]=y[i];
for(t=x,x=y,y=t,x[sa[]]=,i=,p=;i<n;i++)
x[sa[i]]=cmp(y,sa[i],sa[i-],j)?p-:p++;
}
} void Lcp(int n){
int i,j,k=;
for(i=;i<=n;i++)rank[sa[i]]=i;
for(i=;i<n;lcp[rank[i++]]=k)
for(k?--k:k,j=sa[rank[i]-];r[j+k]==r[i+k];++k);
}
int Qmin(int l,int r){
return min(Min[l][mm[r-l+]],Min[r-(<<mm[r-l+])+][mm[r-l+]]);
} int Query(int p,int l){
int ret=,lo,hi;
hi=rank[p];lo=;
while(lo<=hi){
int mid=(lo+hi)>>;
if(Qmin(mid,rank[p])>=l)hi=mid-;
else lo=mid+;
}
ret+=rank[p]-lo+; hi=len;lo=rank[p]+;
while(lo<=hi){
int mid=(lo+hi)>>;
if(Qmin(rank[p]+,mid)>=l)lo=mid+;
else hi=mid-;
}
return ret+hi-rank[p];
} int main(){
scanf("%s",s);
len=strlen(s);
int l=;
S[l++]='#';S[l++]='&';
for(int i=;i<len;i++){
pos[l]=i;
S[l++]=s[i];
pos[l]=i+;
S[l++]='&';
}
S[l]=;
int mx=,id;
for(int i=;i<l;i++){
maxl[i]=mx>i?min(maxl[id*-i],mx-i):;
while(S[i+maxl[i]]==S[i-maxl[i]])maxl[i]++;
if(i+maxl[i]>mx){
mx=i+maxl[i];
id=i;
}
} for(int i=;i<=l;i++)
maxL[pos[i-maxl[i]+]]=max(maxL[pos[i-maxl[i]+]],maxl[i]-);
//maxL[i]原s字符串中i位置最长回文子串
for(int i=;i<len;i++)r[i]=s[i];
DA(len+,);
Lcp(len); mm[]=-;
for(int i=;i<=len;i++){
mm[i]=(i&(i-))?mm[i-]:mm[i-]+;
Min[i][]=lcp[i];
} for(int k=;k<=mm[len];k++)
for(int i=;i+(<<k)-<=len;i++)
Min[i][k]=min(Min[i][k-],Min[i+(<<(k-))][k-]); long long ans=;
for(int i=;i<len;i++){
ans=max(ans,1ll*maxL[i]*Query(i,maxL[i]));
}
printf("%lld\n",ans);
return ;
}
然后还有一种方法,前面提过,回文自动机:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=;
int last,cnt,ch[maxn][],fail[maxn],len[maxn],num[maxn];
long long ans=;
char s[maxn];
int find(int i,int x){
while(s[i-len[x]-]!=s[i])x=fail[x];
return x;
}
int main(){
scanf("%s",s);
fail[]=;len[]=-;cnt=;
for(int i=;s[i];i++){
int j=find(i,last);
if(ch[j][s[i]-'a'])
last=ch[j][s[i]-'a'];
else{
len[last=++cnt]=len[j]+;
fail[last]=ch[find(i,fail[j])][s[i]-'a'];
ch[j][s[i]-'a']=last;
}
num[last]++;
}
for(int i=cnt;i>=;i--)
ans=max(ans,1ll*num[i]*len[i]),num[fail[i]]+=num[i];
printf("%lld\n",ans);
return ;
}
超级短,超级简单,但还是有些地方要注意:
14行的初始化不能乱改,改了挺麻烦的;21,22行是有顺序的,写反了会死循环。
字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串的更多相关文章
- BZOJ 3676: [Apio2014]回文串
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 2013 Solved: 863[Submit][Status ...
- bzoj 3676: [Apio2014]回文串 回文自动机
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 844 Solved: 331[Submit][Status] ...
- BZOJ 3676 [Apio2014]回文串 (后缀自动机+manacher/回文自动机)
题目大意: 给你一个字符串,求其中回文子串的长度*出现次数的最大值 明明是PAM裸题我干嘛要用SAM做 回文子串有一个神奇的性质,一个字符串本质不同的回文子串个数是$O(n)$级别的 用$manach ...
- ●BZOJ 3676 [Apio2014]回文串
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3676 题解: 后缀数组,Manacher,二分 首先有一个结论:一个串的本质不同的回文串的个 ...
- BZOJ 3676 [Apio2014]回文串(回文树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题目大意] 考虑一个只包含小写拉丁字母的字符串s. 我们定义s的一个子串t的& ...
- bzoj 3676 [Apio2014]回文串(Manacher+SAM)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题意] 给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串. ...
- BZOJ.3676.[APIO2014]回文串(回文树)
BZOJ 洛谷 很久之前写(抄)过一个Hash+Manacher的做法,当时十分懵逼=-= 然而是道回文树模板题. 回文树教程可以看这里(真的挺妙的). 顺便再放上MilkyWay的笔记~ //351 ...
- BZOJ 3676: [Apio2014]回文串 后缀自动机 Manacher 倍增
http://www.lydsy.com/JudgeOnline/problem.php?id=3676 过程很艰难了,第一次提交Manacher忘了更新p数组,超时,第二次是倍增的第0维直接在自动机 ...
- bzoj 3676: [Apio2014]回文串【后缀自动机+manacher】
用manacher找出本质不同的回文子串放在SAM上跑 #include<iostream> #include<cstdio> #include<cstring> ...
随机推荐
- C#生成验证码实例
常用生成验证码实例封装: /// <summary> /// 生成内存位图 /// </summary> /// <param name="Code" ...
- HTML5 文件域+FileReader 读取文件(二)
一.读取文本文件内容,指定字符编码 <div class="container"> <!--文本文件验证--> <input type="f ...
- java判断字符串是否为空的方法总结
http://blog.csdn.net/qq799499343/article/details/8492672 以下是java 判断字符串是否为空的四种方法: 方法一: 最多人使用的一个方法, 直观 ...
- Oracle分区表学习
(1) 表空间及分区表的概念表空间: 是一个或多个数据文件的集合,所有的数据对象都存放在指定的表空间中,但主要存放的是表, 所以称作表空间.分区表: 当表中的数据量不断增大,查询数据的速度就会变慢,应 ...
- 华硕_ZX50JX4200 安装ssd固态盘
本人亲身的一次经历,帮朋友的华硕手提装一个内存和ssd固态 内存5分钟搞定,但是ssd固态盘就经过了一番的折腾 首先要拧掉所有后盖的螺丝,把光驱拆下来,注意撬开键盘板的时候有排线,不能弄断了.然后一定 ...
- 【vc】14_网络编程_socket编程
1.计算机网络基本知识 最简单的网络程序如图: 提示:IP地址就相当于一个公司的总机号码,端口号就相当于分机号码.在打电话时,拨通总机后,还需要转到分机上. (1)协议 ·为进行网络中的数据交换(通信 ...
- modifytime是一个神奇的column name----这边文章是错的totally,因为我的实验不彻底。timestamp属性很神奇,头一个timestamp,会自动的成DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
在mysql里边modifytime是一个神奇的column name,试一下. 请执行sql语句 CREATE TABLE `test_time` ( `modifytime` timestamp ...
- 自构BeanHandler(用BeansUtils)
class BeanHandler<T> implements ResultSetHandler<T>{ private Class<T> clazz; publi ...
- PHP替换中文字符
mb_regex_encoding('utf-8');$htmlNavSubmenu2 = str_replace('<li id="w-menu-food-334"> ...
- js四舍五入的bug和方法
简单来说js使用原生toFixed(x)截取小数的时候会有误差,出现在比如var o = 0.3303;o.toFixed(3);//0.330 toFixed(x)本来也是一个获取四舍五入的截取方法 ...