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 ...
随机推荐
- shelll中test命令的使用【转】
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值.字符和文件三个方面的测试. 数值测试 参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt 大于则为真 -ge 大于等于 ...
- es6 Array.from + new Set 去重复
// es6 set数据结构 生成一个数据集 里面的元素是唯一的 const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]); // items 是个对象 item ...
- python模块详解 | filecmp
简介: filecmp是python内置的一个模块,用于比较文件及文件夹的内容,它是一个轻量级的工具,使用非常简单 两个主要的方法: filecmp.cmp(f1, f2[, shallow]) 比较 ...
- Lambda表达式你会用吗?
函数式编程 在正式学习Lambda之前,我们先来了解一下什么是函数式编程 我们先看看什么是函数.函数是一种最基本的任务,一个大型程序就是一个顶层函数调用若干底层函数,这些被调用的函数又可以调用其他函数 ...
- 映泰主板H100系列安装win7的各种坑
自100系列主板发布以来,windows7好像就被遗弃一样,原因就在于安装win7的时候,会出现USB设备无法使用导致无法安装的问题.主要在于Win7系统没有整合USB的XHCI驱动,而100系列芯片 ...
- ctfhub技能树—sql注入—UA注入
手注 打开靶机 查看页面信息 抓取数据包 根据提示注入点在User-Agent文件头中 开始尝试注入 成功查到数据库名 查询数据表名 查询字段名 查询字段信息 成功拿到flag 盲注 测试是否存在时间 ...
- ABAP program lines are wider than the internal table
错误详细描述: An exception occurred that is explained in detail below.The exception, which is assigned to ...
- migo的BAPI示例BAPI_GOODSMVT_CREATE
1 *&---------------------------------------------------------------------* 2 *& Report Z_BAP ...
- java 日期与时间操作
我们先来了解一下基本的概念 日期 2020-11-21 2020-11-22 时间 15:36:43 2020-11-21 15:36:43 时区 北京时间 2020-11-21 15:36:43 东 ...
- Java基础复习3
循环的嵌套 public class demo8 { public static void main(String[] args) { /* 输出######## ...