BZOJ3238 [Ahoi2013]差异 【SAM or SA】
BZOJ3238 [Ahoi2013]差异
给定一个串,问其任意两个后缀的最长公共前缀长度的和
1.又是后缀,又是\(lcp\),很显然直接拿\(SA\)的\(height\)数组搞就好了,配合一下单调栈
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const int MAXN = 5e5+7;
struct SA{
int sa[MAXN],rk[MAXN],c[MAXN],sec[MAXN],height[MAXN],n;
char s[MAXN];
void getsa(int m){
n = strlen(s+1);
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rk[i]=s[i]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[rk[i]]--] = i;
for(int k = 1; k <= n; k <<= 1){
int p = 0;
for(int i = n - k + 1; i <= n; i++) sec[++p] = i;
for(int i = 1; i <= n; i++) if(sa[i]>k) sec[++p] = sa[i] - k;
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rk[sec[i]]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[rk[sec[i]]]--] = sec[i];
p = 0;
swap(rk,sec);
rk[sa[1]] = ++p;
for(int i = 2; i <= n; i++) rk[sa[i]] = sec[sa[i]]==sec[sa[i-1]] and sec[sa[i]+k]==sec[sa[i-1]+k] ? p : ++p;
if(p==n) break;
m = p;
}
}
void getheight(){
int k = 0;
for(int i = 1; i <= n; i++){
if(k) k--;
int j = sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rk[i]] = k;
}
}
LL solve(){
getsa(128);
getheight();
LL ret = (n+1ll) * n * (n-1ll) / 2;
stack<pair<int,int> > stk;
LL tot = 0;
for(int i = 1; i <= n; i++){
int num = 1;
while(!stk.empty() and height[i]<=height[stk.top().first]){
num += stk.top().second;
tot -= 1ll * stk.top().second * (height[stk.top().first] - height[i]);
stk.pop();
}
stk.push(make_pair(i,num));
tot += height[i];
ret -= tot * 2;
}
return ret;
}
}sa;
char s[MAXN];
int main(){
scanf("%s",sa.s+1);
printf("%lld\n",sa.solve());
return 0;
}
2.考虑用\(SAM\)来做,把串反转一下,现在要计算的是任意两个前缀的最长公共后缀的和
\(SAM\)有个性质,那就是两个点的\(LCA\)的状态表示的最长串,是这两个点表示的子串的最长公共后缀
所以我们可以枚举所有的\(LCA\),然后计算每一个\(LCA\)的贡献,先计算其任意两棵子树对应的贡献,再计算子树和当前节点产生的贡献
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
using LL = int_fast64_t;
const int MAXN = 1e6+7;
struct SAM{
int len[MAXN],link[MAXN],ch[MAXN][26],cnt[MAXN],tot,last;
vector<int> G[MAXN];
SAM(){ link[0] = -1; }
void extend(int c){
int np = ++tot, p = last;
len[np] = len[last] + 1; cnt[np] = 1;
while(p!=-1 and !ch[p][c]){
ch[p][c] = np;
p = link[p];
}
if(p==-1) link[np] = 0;
else{
int q = ch[p][c];
if(len[p]+1==len[q]) link[np] = q;
else{
int clone = ++tot;
len[clone] = len[p] + 1;
link[clone] = link[q];
memcpy(ch[clone],ch[q],sizeof(ch[q]));
link[np] = link[q] = clone;
while(p!=-1 and ch[p][c]==q){
ch[p][c] = clone;
p = link[p];
}
}
}
last = np;
}
void dfs(int u, LL &ret){
int all = 0;
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
dfs(v,ret);
all += cnt[v];
}
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
ret -= 1ll * (all - cnt[v]) * cnt[v] * len[u];
}
ret -= 2ll * all * cnt[u] * len[u];
cnt[u] += all;
}
void solve(char *s){
int n = strlen(s);
for(int i = 0; i < n; i++) extend(s[i]-'a');
for(int i = 1; i <= tot; i++) G[link[i]].push_back(i);
LL ret = n * (n-1ll) * (n+1ll) / 2;
dfs(0,ret);
printf("%lld\n",ret);
}
}sam;
char s[MAXN];
int main(){
scanf("%s",s);
sam.solve(s);
return 0;
}
BZOJ3238 [Ahoi2013]差异 【SAM or SA】的更多相关文章
- BZOJ3238:[AHOI2013]差异(SAM)
Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...
- bzoj3238 [Ahoi2013]差异 后缀数组+单调栈
[bzoj3238][Ahoi2013]差异 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Ou ...
- [bzoj3238][Ahoi2013]差异_后缀数组_单调栈
差异 bzoj-3238 Ahoi-2013 题目大意:求任意两个后缀之间的$LCP$的和. 注释:$1\le length \le 5\cdot 10^5$. 想法: 两个后缀之间的$LCP$和显然 ...
- luogu P4248 [AHOI2013]差异 SAM
luogu P4248 [AHOI2013]差异 链接 luogu 思路 \(\sum\limits_{1<=i<j<=n}{{len}(T_i)+{len}(T_j)-2*{lcp ...
- BZOJ3238 [Ahoi2013]差异 SA+单调栈
题面 戳这里 题解 考虑把要求的那个东西拆开算,前面一个东西像想怎么算怎么算,后面那个东西在建出\(height\)数组后相当于是求所有区间\(min\)的和*2,单调栈维护一波即可. #includ ...
- BZOJ3238: [Ahoi2013]差异 (后缀自动机)
Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...
- [BZOJ3238][AHOI2013]差异(后缀数组)
求和式的前两项可以直接算,问题是对于每对i,j计算LCP. 一个比较显然的性质是,LCP(i,j)是h[rk[i]+1~rk[j]]中的最小值. 从h的每个元素角度考虑,就是对每个h计算有多少对i,j ...
- [BZOJ3238][Ahoi2013]差异解题报告|后缀数组
Description 先分析一下题目,我们显然可以直接算出sigma(len[Ti]+len[Tj])的值=(n-1)*n*(n+1)/2 接着就要去算这个字符串中所有后缀的两两最长公共前缀总和 首 ...
- BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】
题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...
随机推荐
- 【C++】《C++ Primer 》第十九章
第十九章 特殊工具与技术 一.控制内存分配 1. 重载new和delete new表达式的工作机理: string *sp = new string("a value"); //分 ...
- SpringBoot配置文件(1)
配置文件 1.配置文件 SpringBoot使用一个全局的配置文件 application.properties application.yml 配置文件名是固定的: 他的作用是修改SpringBoo ...
- Nginx Consul nginx-upsync-module
nginx consul nginx-upsync-module 依赖包: yum -y install libpcre3 libpcre3-dev ruby zlib1g-dev patch 下载n ...
- unixbench性能测试跑分工具
UnixBench是一个类unix系(Unix,BSD,Linux)统下的性能测试工具,一个开源工具,被广泛用与测试linux系统主机的性能 所谓跑分工具,不仅各项的测试有得分,最后跑完也会有一个综合 ...
- Springboot之文件监控
背景:在实际环境部署构成中,由于特殊网络环境因素,有很多服务器之间的网络都是单向的,不能互相访问的,只有通过特定技术手段做到文件的单项摆渡,这就需要在两台服务器上分别写序列化程序和反序列化程序,这里不 ...
- MongoDB查询优化--explain,慢日志
引入 与Mysql数据库一样,MongoDB也有自己的查询优化工具,explain和慢日志 explain shell命令格式 db.collection.explain().<method(. ...
- 【AtCoder Beginner Contest 181】A~F题解
越学越菜系列 于2020.11.2,我绿了(错乱) A - Heavy Rotation 签到题,奇数Black,偶数White. code: #include<bits/stdc++.h> ...
- Kubernetes调整Node节点快速驱逐pod的时间
在高可用的k8s集群中,当Node节点挂掉,kubelet无法提供工作的时候,pod将会自动调度到其他的节点上去,而调度到节点上的时间需要我们慎重考量,因为它决定了生产的稳定性.可靠性,更快的迁移可以 ...
- Java并发包源码学习系列:阻塞队列BlockingQueue及实现原理分析
目录 本篇要点 什么是阻塞队列 阻塞队列提供的方法 阻塞队列的七种实现 TransferQueue和BlockingQueue的区别 1.ArrayBlockingQueue 2.LinkedBloc ...
- 最佳的思维导图生成工具——markmap 使用教程
前言 相信很多程序员朋友都有在用 Markdown 吧,我是大三找实习工作的时候接触到的,简历就是用 Markdown 写的. Markdown 的好处是专注码字的同时还能兼顾排版,不用像 word ...