洛谷2408不同字串个数/SPOJ 694/705 (后缀数组SA)
真是一个三倍经验好题啊。
我们来观察这个题目,首先如果直接整体计算,怕是不太好计算。
首先,我们可以将每个子串都看成一个后缀的的前缀。那我们就可以考虑一个一个后缀来计算了。
为了方便起见,我们选择按照字典序来一次插入每个后缀,然后每次考虑当前后缀会产生的新串和与之前插入的串重复的串(这里之所以可以这么考虑,是因为如果他会对后面的串产生重复的话,那么会在后面那个串加入的时候计算的)
那么我们考虑,一个排名为\(i\)的后缀,插入之后不考虑重复的话,会新增多少个子串呢?
不难发现是\(n-sa[i]+1\)个(注意后缀的位置编号是从前开始,而后缀的贡献是后面的子串个数。
那么重复的该怎么计算呢?
我们发现重复的部分实际是当前这个后缀和之前的后缀的\(lcp\)部分会重复,而且应该是最大的\(lcp\) (如果取小的会算少,直接求sum会算多)。
而有一个比较经典的性质就是,在字典序\(1到i\)中与\(i\)的\(lcp\)长度最长的,一定是\(i-1\),这里有两种理解方式,一个是越远差距越大,另一种是越靠前,取\(min\)的范围越大,\(min\)就会可能越小
那么枚举+计算,记得开\(long \ long\)就三倍经验辣
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
int rk[maxn],sa[maxn];
int wb[maxn];
int tmp[maxn];
char a[maxn];
int n;
int h[maxn],height[maxn];
void getsa()
{
int *x=rk,*y=tmp;
int s=128;
int p=0;
for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
for (int i=1;i<=s;i++) wb[i]=0;
for (int i=1;i<=n;i++) wb[x[y[i]]]++;
for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
for (int j=1;p<n;j<<=1)
{
p=0;
for (int i=n-j+1;i<=n;i++) y[++p]=i;
for (int i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
for (int i=1;i<=s;i++) wb[i]=0;
for (int i=1;i<=n;i++) wb[x[y[i]]]++;
for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
swap(x,y);
p=1;
x[sa[1]]=1;
for (int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p;
s=p;
}
for (int i=1;i<=n;i++) rk[sa[i]]=i;
h[0]=0;
for (int i=1;i<=n;i++)
{
h[i]=max(h[i-1]-1,0);
while(i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
}
for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
int t;
void init()
{
memset(wb,0,sizeof(wb));
memset(rk,0,sizeof(rk));
memset(sa,0,sizeof(sa));
memset(tmp,0,sizeof(tmp));
memset(h,0,sizeof(h));
memset(height,0,sizeof(height));
}
int main()
{
//cin>>t;
//while (t--)
//{
n=read();
init();
scanf("%s",a+1);
getsa();
long long ans=0;
for (int i=1;i<=n;i++)
{
ans=ans+(long long)(n-sa[i]+1)-(long long)h[i];//这里可以理解成我们顺着字典序的顺序,加入每个后缀,将子串看成后缀的前缀
// 而每次加入会产生新的n-sa[i]+1个字串,其中重复的就是和之前的子串的某些lcp,而字典序上,在这个串前面,与某个串lcp最长的应该是i-1那个串(这里可以理解成越往前差距越大)
}
cout<<ans<<"\n";
// }
return 0;
}
洛谷2408不同字串个数/SPOJ 694/705 (后缀数组SA)的更多相关文章
- 洛谷P2408 不同字串个数 [后缀数组]
题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...
- SPOJ 694/705 后缀数组
思路: 论文题*n Σn-i-ht[i]+1 就是结果 O(n)搞定~ //By SiriusRen #include <cstdio> #include <cstring> ...
- 【题解】洛谷P1032 [NOIP2002TG]字串变换(BFS+字符串)
洛谷P1032:https://www.luogu.org/problemnew/show/P1032 思路 初看题目觉得挺简单的一道题 但是仔细想了一下发现实现代码挺麻烦的 而且2002年的毒瘤输入 ...
- 【洛谷】P1032 字串变换
题目地址:https://www.luogu.org/problemnew/show/P1032 洛谷训练场BFS的训练题呀. “BFS不就是用队列的思想去遍历一切情况嘛.我已经不是小孩子了,我肯定能 ...
- 洛谷 P1032 【字串变换】
感觉这个题用一些常用的stl和string函数会非常简单..(难道就是考这两个的吗? vector<pair<string,string>>pos//用于变化 map<s ...
- 洛谷P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)
我学AC自动机的时候就看到了这题,想用AC自动机结果被学长码风劝退-- 学后缀数组时又看到了这题--那就写写后缀数组做法吧 结果码风貌似比当年劝退我的学长还毒瘤啊 对所有的模式串+询问串,不同串之间用 ...
- 【洛谷4770/UOJ395】[NOI2018]你的名字(后缀数组_线段树合并)
题目: 洛谷4770 UOJ395 分析: 一个很好的SAM应用题-- 一句话题意:给定一个字符串\(S\).每次询问给定字符串\(T\)和两个整数\(l\).\(r\),求\(T\)有多少个本质不同 ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 【洛谷P3411】字串变换
题解:普通的 BFS 没什么可说的,字符串处理是这道题的难点,同时需要注意哈希判重. 另外,对于 \(string\) 类来说,学到了一个 push_back((char)) 操作. c++strin ...
随机推荐
- 【C语言】第1章 程序设计与C语言
第1章 程序设计与C语言 程序:一组计算机能识别和执行的 指令. 计算机语言:人和计算机交流信息的.计算机和人都能识别的语言 C语言允许用两种注释方式: //:单行注释 可单独占一行 可出现在一行中其 ...
- deepin-terminal改造风云再起
1. 创作背景 使用deepin-terminal的时候,我发现一些小的问题. 在论坛的帖子(https://bbs.deepin.org/zh/post/224502)也总结反馈了这些问题 终端标签 ...
- JobExecutionContext中的JobDataMapjob与Detail与Trigger中的JobDataMapjob
public static void main(String[] args) { //配置模式 build模式 //1.实例一个JOB JobDetail jobDetail = JobBuilder ...
- JavaSE-Java基础面试题
重载与重写的区别 重载:本类中,方法名相同,参数列表不同,(参数类型.参数顺序.参数个数),返回值类型可以不同,访问修饰符可不同 重写:子类中,方法名相同,参数不能改,返回值类型一致或其子类,访问权限 ...
- SSH无法正常连接服务器
远程权限没有打开 #允许root登录 PermitRootLogin yes #不允许空密码登录 PermitEmptyPasswords no 远端的ssh信息有变化,本地保存的那个需要删掉 Use ...
- Mybatis-plus<二>通用CRUD,分页
Mybatis-plus<二>通用CRUD,分页 与博客Mybatis-plus<一>为同一个Springboot项目. Demo GitHub下载地址:https://git ...
- Linux常用命令 - tail命令详解
21篇测试必备的Linux常用命令,每天敲一篇,每次敲三遍,每月一循环,全都可记住!! https://www.cnblogs.com/poloyy/category/1672457.html 实时刷 ...
- 【机器学习|数学基础】Mathematics for Machine Learning系列之线性代数(1):二阶与三阶行列式、全排列及其逆序数
@ 目录 前言 二阶与三阶行列式 二阶行列式 三阶行列式 全排列及其逆序数 全排列 逆序数 结语 前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍 ...
- Tars | 第6篇 基于TarsGo Subset路由规则的Java JDK实现方式(下)
目录 前言 1. 修改.tars协议文件 1.1 Java源码位置及逻辑分析 1.2 Java语言实现方式 1.3 通过协议文件自动生成代码 1.4 变更代码的路径 2. [核心]增添Subset核心 ...
- el-upload + accept限制上传的文件格式
/** * kevin 2021/1/4 * @description el-upload + accept限制上传的文件格式 * @param e 校验的类型 * @returns {str ...