bzoj3676 [Apio2014]回文串 卡常+SAM+树上倍增
bzoj3676 [Apio2014]回文串 SAM+树上倍增
链接
思路
根据manacher可以知道,每次暴力扩展才有可能出现新的回文串。
所以推出本质不同的回文串个数是O(n)级别的。
每次查询一个串出现的个数。
建立出parent树,一个串出现的个数就是对应parent树上的所在的子树的大小,利用树上倍增可以log求出一个串出现的个数。
具体一点,我们知道endpos,可以找到对应parent tree的节点。
然后目标节点肯定是在根到此节点的路径上的(他是她的后缀嘛)。
用子串长度和节点的longest比较就行了,倍增慢慢跳。
总的复杂度\(O(nlogn)\)
当然还有更简单更优秀的的回文自动机。
卡常!!
脸丑的bzoj,luogu会MLE。
所以利用我你们的卡常技巧
parent树的树高不会太高,数据中应该深度都小于1000,倍增开10倍。(有点面向数据了)
sam状态是3*n,用map或unordered_map牺牲一点复杂度去省空间(测得用map毫无用处,内存增大)。
当然,可以不用建立出parent tree的图,因为你已经有了父节点表示法的图了。
老方法:基数排序endpos去更新parent tree的size
还是wxy super cool.我再也不建parent树了,vector内存太大了。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=600007;
int read() {
    int x=0,f=1;char s=getchar();
    for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;
}
int n,len,p[N],st[N][11],c[N],a[N];
char s[N],S[N];
struct node {
    int len,fa,ch[26];
}dian[N];
int siz[N],tot=1,las=1,i_can_find_it[N];
void add(int c,int k_th) {
    int p=las;int np=las=++tot;
    dian[np].len=dian[p].len+1;
    for(;p&&!dian[p].ch[c];p=dian[p].fa) dian[p].ch[c]=np;
    if(!p) dian[np].fa=1;
    else {
        int q=dian[p].ch[c];
        if(dian[q].len==dian[p].len+1) dian[np].fa=q;
        else {
            int nq=++tot;dian[nq]=dian[q];
            dian[nq].len=dian[p].len+1;
            dian[q].fa=dian[np].fa=nq;
            for(;p&&dian[p].ch[c]==q;p=dian[p].fa)
                dian[p].ch[c]=nq;
        }
    }
    siz[las]=1;
    i_can_find_it[k_th]=las;
}
int query(int l,int r) {
    int u=i_can_find_it[r];
    for(int i=10;i>=0;--i)
        if(dian[st[u][i]].len>=r-l+1) u=st[u][i];
    return siz[u];
}
int main() {
// 	freopen("testdata.in","r",stdin);
    scanf("%s",s+1);
    n=strlen(s+1);
    //build sam
    for(int i=1;i<=n;++i) add(s[i]-'a',i);
    //build parent tree
    for(int i=2;i<=tot;++i) st[i][0]=dian[i].fa;
    for(int i=1;i<=tot;++i) c[dian[i].len]++;
    for(int i=1;i<=tot;++i) c[i]+=c[i-1];
    for(int i=1;i<=tot;++i) a[c[dian[i].len]--]=i;
    for(int i=tot;i>=1;--i) siz[dian[a[i]].fa]+=siz[a[i]];
    //init st
    for(int j=1;j<=10;++j)
        for(int i=1;i<=n;++i)
            st[i][j]=st[st[i][j-1]][j-1];
    //manacher -- init
    S[0]='@';
    for(int i=1;i<=n+n;i+=2) S[i]='#',S[i+1]=s[i/2+1];
    S[len=n+n+1]='#';
    int id=0,mx=0;
    ll ans=0;
    //manacher -- do
    for(int i=1;i<=len;++i) {
        if(i<mx) p[i]=min(mx-i,p[id*2-i]);
        else p[i]=1;
        while(S[i+p[i]]==S[i-p[i]]) {
            int l=(i-p[i]+1)/2,r=(i+p[i])/2;
            if(l<=r) ans=max(ans,1LL*(r-l+1)*query(l,r));
            p[i]++;
        }
        if(mx < i+p[i]-1) mx=i+p[i]-1,id=i;
    }
    //print
    printf("%lld\n",ans);
    return 0;
}
bzoj3676 [Apio2014]回文串 卡常+SAM+树上倍增的更多相关文章
- [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串
		回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ... 
- [BZOJ3676][APIO2014]回文串(Manacher+SAM)
		3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3097 Solved: 1408[Submit][Statu ... 
- [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)
		3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3396 Solved: 1568[Submit][Statu ... 
- bzoj 3676 [Apio2014]回文串(Manacher+SAM)
		[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题意] 给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串. ... 
- [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增
		Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ... 
- BZOJ3676: [Apio2014]回文串(SAM+Manacher/PAM)
		Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行 ... 
- BZOJ3676 APIO2014回文串(manacher+后缀自动机)
		由于本质不同的回文子串数量是O(n)的,考虑在对于每个回文子串在第一次找到它时对其暴力统计.可以发现manacher时若右端点移动则找到了一个新回文串.注意这样会漏掉串长为1的情况,特判一下. 现在问 ... 
- 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)
		传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ... 
- BZOJ3676[Apio2014]回文串——回文自动机
		题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. 输入 输入只有一行,为一个只包含小写字 ... 
随机推荐
- .Net Core 获取应用物理路径的常见问题
			如果要得到传统的ASP.Net应用程序中的相对路径或虚拟路径对应的服务器物理路径,只需要使用使用Server.MapPath()方法来取得Asp.Net根目录的物理路径. 但是在Asp.Net Cor ... 
- Delphi面向对象的编程思想
			第一章.建立面向对象的新思维 1.1.1历史背景 目前对象技术的前沿课题包括设计模式.分布式对象系统.和基于网络的对象应用等 目前面向对象的语言包含4个基本的分支: 1.基于Smalltalk的:包括 ... 
- RabbitMQ学习之Routing(4)
			上一节,是广播日志message到很多的receivers. 这节,我们讲订阅其中的一个子集.例如,我们想可以把危机的error message导到log file.而仍然可以打印所有的log mes ... 
- 【翻译】nginx初学者指南
			nginx初学者指南 本文翻译自nginx官方网站:http://nginx.org/en/docs/beginners_guide.html#control 该指南会对nginx做一个简要的介绍,同 ... 
- 原生js实现模块来回拖拽效果
			代码比较冗余,还没来得及做整理,往见谅. 主要用到的 JS 事件有: onmousedown:鼠标点下事件 onmousemove:鼠标移动事件 onmouseup:鼠标放开事件 具体代码如下: &l ... 
- FreePascal - CodeTyphon 如何调整代码编辑器背景色?
			当前版本的CodeTyphon默认背景色是黑色,看起来很不习惯,通过下面操作,修改了它的代码编辑器的背景色: 1,打开CodeTyphon的菜单“工具”->“选项”. 2,选择左侧列表项目“颜色 ... 
- XenCenter安装VM
			XenServer是服务器"虚拟化系统".系统设置为Linux_x86-64即可安装XenServer 和VMware ESX/ESXi有点不同的是,XenServer 不能在Xe ... 
- Android-----实现给图片添加字体
			实现给图片添加字体,图片旋转功能:xml布局文件内容如下,一个简单的ImageView布局 <com.example.hsjgapp.RotateImageView //这里存放要展示的图片 a ... 
- ML-求解 SVM 的SMO 算法
			这算是我真正意义上认真去读的第一篇ML论文了, but, 我还是很多地方没有搞懂, 想想, 缓缓吧, 还是先熟练调用API 哈哈 原论文地址: https://www.microsoft.com/en ... 
- MyBatis面试题整理
			MyBatis面试题整理 1.什么是MyBatis? 答:MyBatis是一个可以自定义SQL.存储过程和高级映射的持久层框架. 2.讲下MyBatis的缓存 答:MyBatis的缓存分为一级缓存和二 ... 
