真是一个三倍经验好题啊。

我们来观察这个题目,首先如果直接整体计算,怕是不太好计算。

首先,我们可以将每个子串都看成一个后缀的的前缀。那我们就可以考虑一个一个后缀来计算了。

为了方便起见,我们选择按照字典序来一次插入每个后缀,然后每次考虑当前后缀会产生的新串和与之前插入的串重复的串(这里之所以可以这么考虑,是因为如果他会对后面的串产生重复的话,那么会在后面那个串加入的时候计算的)

那么我们考虑,一个排名为\(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)的更多相关文章

  1. 洛谷P2408 不同字串个数 [后缀数组]

    题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...

  2. SPOJ 694/705 后缀数组

    思路: 论文题*n Σn-i-ht[i]+1 就是结果 O(n)搞定~ //By SiriusRen #include <cstdio> #include <cstring> ...

  3. 【题解】洛谷P1032 [NOIP2002TG]字串变换(BFS+字符串)

    洛谷P1032:https://www.luogu.org/problemnew/show/P1032 思路 初看题目觉得挺简单的一道题 但是仔细想了一下发现实现代码挺麻烦的 而且2002年的毒瘤输入 ...

  4. 【洛谷】P1032 字串变换

    题目地址:https://www.luogu.org/problemnew/show/P1032 洛谷训练场BFS的训练题呀. “BFS不就是用队列的思想去遍历一切情况嘛.我已经不是小孩子了,我肯定能 ...

  5. 洛谷 P1032 【字串变换】

    感觉这个题用一些常用的stl和string函数会非常简单..(难道就是考这两个的吗? vector<pair<string,string>>pos//用于变化 map<s ...

  6. 洛谷P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)

    我学AC自动机的时候就看到了这题,想用AC自动机结果被学长码风劝退-- 学后缀数组时又看到了这题--那就写写后缀数组做法吧 结果码风貌似比当年劝退我的学长还毒瘤啊 对所有的模式串+询问串,不同串之间用 ...

  7. 【洛谷4770/UOJ395】[NOI2018]你的名字(后缀数组_线段树合并)

    题目: 洛谷4770 UOJ395 分析: 一个很好的SAM应用题-- 一句话题意:给定一个字符串\(S\).每次询问给定字符串\(T\)和两个整数\(l\).\(r\),求\(T\)有多少个本质不同 ...

  8. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  9. 【洛谷P3411】字串变换

    题解:普通的 BFS 没什么可说的,字符串处理是这道题的难点,同时需要注意哈希判重. 另外,对于 \(string\) 类来说,学到了一个 push_back((char)) 操作. c++strin ...

随机推荐

  1. C# - 习题04_分析代码写出结果i1、i2、c.i、str、c.str

    时间:2017-08-23 整理:byzqy 题目:分析如下代码,写出程序输出结果. 文件:Class1.cs using System; namespace Interview3 { class C ...

  2. Learning to Compare: Relation Network for Few-Shot Learning 论文笔记

    主要原理: 和Siamese Neural Networks一样,将分类问题转换成两个输入的相似性问题. 和Siamese Neural Networks不同的是: Relation Network中 ...

  3. 5M1E,软件质量管理最佳解决方案

    - 如何做好一个产品? - 用户.需求.文化.价值.设计.流程,这些因素缺一不可.- 那么,如何做好产品的质量管理?- 人.机器.物料.方法.环境.测量,这些因素同样缺一不可.能够影响产品质量波动的因 ...

  4. 使用 IDEA 配合 Dockerfile 部署 SpringBoot 工程

    准备 SpringBoot 工程 新建 SpringBoot 项目,默认的端口是 8080 ,新建 Controller 和 Mapping @RestController public class ...

  5. python实现遥感图像阈值分割

    1.阈值分割 import os import cv2 import numpy as np import matplotlib.pyplot as plt from osgeo import gda ...

  6. Spring AOP 事务配置(实现转账事务)

    1. 事务特性 事务特性:ACID 原子性:整体 [原子性是指事务包含的所有操作要么全部成功,要么全部失败] 一致性:数据 [一个事务执行之前和执行之后都必须处于一致性状态] 隔离性:并发 [对于任意 ...

  7. 使用shell脚本实现everthing的功能

    我们知道,在 Windows 下,有一款非常实用的神器,叫作 Everything ,它可以在极短的时间里,搜索出来你所想要的文件/目录,如下图示: Linux 下也有一些类似于 everything ...

  8. C语言实现有限状态机

    1. 什么是有限状态机 有限状态机在百度百科上的解释为: 有限状态自动机(FSM "finite state machine" 或者FSA "finite state a ...

  9. 使用Dockerfile Maven插件

    我们常见开源项目中使用的Docker Maven插件是com.spotify:docker-maven-plugin.可用版本信息见Github. 通过其介绍可知该插件已经不再推荐使用,取而代之的是c ...

  10. c++ undefined reference

    记录一次c++编程时发现的问题 报错 undefined reference undefined reference to `Student::~Student()' 下面还有类似的好几行,翻译过来就 ...