luoguP2408不同子串个数
传送门
解法一:后缀数组
可以知道每一个子串都是后缀的前缀,那么对于第\(i\)小的后缀的贡献就可以表示为n-sa[i]+1
然而会存在重复的子串,注意height数组的定义,对于sa[i-1]和sa[i],只有height[i]个子串会被重复计算,每次都减掉就好了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e5+10;long long ans;
int n,a[maxn],m='z',x[maxn],y[maxn],num,sa[maxn],rk[maxn],h[maxn];char p[maxn];
int main()
{
read(n);scanf("%s",p+1);
for(rg int i=1;i<=n;i++)a[x[i]=p[i]]++;
for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
for(rg int i=n;i;i--)sa[a[x[i]]--]=i;
for(rg int k=1;k<=n;k<<=1,num=0)
{
for(rg int i=n-k+1;i<=n;i++)y[++num]=i;
for(rg int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(rg int i=1;i<=m;i++)a[i]=0;
for(rg int i=1;i<=n;i++)a[x[i]]++;
for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
for(rg int i=n;i;i--)sa[a[x[y[i]]]--]=y[i];
for(rg int i=1;i<=n;i++)y[i]=x[i];
num=x[sa[1]]=1;
for(rg int i=2;i<=n;i++)
if(y[sa[i]]!=y[sa[i-1]]||y[sa[i]+k]!=y[sa[i-1]+k])x[sa[i]]=++num;
else x[sa[i]]=num;
if(num>=n)break;m=num;
}
for(rg int i=1;i<=n;i++)rk[sa[i]]=i;
for(rg int i=1,k=0,j;i<=n;h[rk[i++]]=k)
for(k=k?k-1:k,j=sa[rk[i]-1];p[j+k]==p[i+k];k++);
for(rg int i=1;i<=n;i++)ans+=n-sa[i]-h[i]+1;printf("%lld\n",ans);
}
解法二:后缀自动机
建出后缀自动机,拓扑排序就行了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
void read(int &x){
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=5e6+10;
int n,tot,las,pre[maxn],nxt[maxn],h[maxn],cnt,in[maxn];
char a[maxn];
long long ans,f[maxn];
struct sam{int len,link,ch[26];}s[maxn];
void sam_pre(){s[0].len=0,s[0].link=-1;}
void ins(int x){
int cur=++tot,p=las;s[cur].len=s[p].len+1;
while(p!=-1&&!s[p].ch[x])s[p].ch[x]=cur,p=s[p].link;
if(p==-1)s[cur].link=0;
else{
int q=s[p].ch[x];
if(s[q].len==s[p].len+1)s[cur].link=q;
else{
int now=++tot;s[now].len=s[p].len+1;
s[now].link=s[q].link;
memcpy(s[now].ch,s[q].ch,sizeof s[q].ch);
while(p!=-1&&s[p].ch[x]==q)s[p].ch[x]=now,p=s[p].link;
s[q].link=s[cur].link=now;
}
}
las=cur;
}
void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
void top_sort(){
queue<int>q;
for(rg int i=0;i<=tot;i++)if(!in[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
ans+=f[x];
for(rg int i=h[x];i;i=nxt[i]){
f[pre[i]]+=f[x];
if(!(--in[pre[i]]))q.push(pre[i]);
}
}
}
int main(){
read(n),scanf("%s",a+1),sam_pre();
for(rg int i=1;i<=n;i++)ins(a[i]-'a');
for(rg int i=0;i<=tot;i++)
for(rg int j=0;j<26;j++)
if(s[i].ch[j])add(i,s[i].ch[j]),in[s[i].ch[j]]++;
f[0]=1,top_sort(),printf("%lld\n",ans-1);
}
luoguP2408不同子串个数的更多相关文章
- [TyvjP1515] 子串统计 [luoguP2408] 不同子串个数(后缀数组)
Tyvj传送门 luogu传送门 经典题 统计一个字符串中不同子串的个数 一个字符串中的所有子串就是所有后缀的前缀 先求出后缀数组,求出后缀数组中相邻两后缀的 lcp 那么按照后缀数组中的顺序遍历求解 ...
- HDU 4622 Reincarnation (查询一段字符串的不同子串个数,后缀自动机)
Reincarnation Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)To ...
- HDU 3948 不同回文子串个数
集训队论文中有求不同子串个数的做法,就是扫一遍height数组,过程中根据height数组进行去重.对于本题也是雷同的,只是每一次不是根据与排名在上一位的LCP去重,而是与上一次统计对答案有贡献的后缀 ...
- HDU4622 (查询一段字符串的不同子串个数,后缀自动机)
http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给出一个字符串和q次询问,每次询问[l,r]区间内不同子串的个数 分析: N<=2000. 我 ...
- [spoj DISUBSTR]后缀数组统计不同子串个数
题目链接:https://vjudge.net/contest/70655#problem/C 后缀数组的又一神奇应用.不同子串的个数,实际上就是所有后缀的不同前缀的个数. 考虑所有的后缀按照rank ...
- ACdream 1430——SETI——————【后缀数组,不重叠重复子串个数】
SETI Time Limit: 4000/2000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) Submit Statist ...
- HDU 5056 Boring count(不超过k个字符的子串个数)
Boring count Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- Luogu P2408 不同子串个数【SAM】
P2408 不同子串个数 计算一个字符串的不同子串个数 两种方法,一种是\(dp\)出来\(SAM\)从起点开始的路径数量 另一种方法就是计算每个点的\(len[i]-len[link[i]]\)这个 ...
- hdu5056(找相同字母不出现k次的子串个数)
题意: 给你一个字符串,然后问你这个字符串里面有多少个满足要求的子串,要求是每个子串相同字母出现的次数不能超过k. 思路: 这种题目做着比较有意思,而且不是很难(但自己还是嘚瑟,w ...
随机推荐
- ABAP OLE常用方法和属性
转自 http://www.cnblogs.com/eric0701/p/5213694.htmlSAP EXCEL OLE常用方法和属性 附加网上找到的比较好的源代码示例一份 1.ole中如何保存和 ...
- 【Windows核心编程】一个使用内存映射文件进行进程间通信的例子
进程间通信的方式有很多种,其底层原理使用的都是内存映射文件. 本文实现了Windows核心编程第五版475页上的demo,即使用内存映射文件来在进程间通信. 进程1 按钮[Create mappin ...
- Go语言string,int,int64 ,float之间类型转换方法
(1)int转string ? 1 2 s := strconv.Itoa(i) 等价于s := strconv.FormatInt(int64(i), 10) (2)int64转string ? 1 ...
- 网络测试常用的命令-比较ping,tracert和pathping等命令之间的关系
无论你是一个网络维护人员,还是正在学习TCP/IP协议,了解和掌握一些常用的网络测试命令将会有助于您更快地检测到网络故障所在,同时也会有助你您了解网络通信的内幕. 下面我们逐步介绍几个常用的命令: 1 ...
- HTML CSS 属性大全
CSS 属性大全 文字属性 「字体族科」(font-family),设定时,需考虑浏览器中有无该字体. 「字体大小」(font-size),注意度量单位.<绝对大小>|<相对大小&g ...
- 使用libcurl进行HTTP GET操作
Working example how to do a GET request with libcurl and save it to a string variable for future use ...
- Python模块:os
OS模块常用用法: os.name() #判断当前使用的系统环境,windows则返回 ‘nt’,Linux则返回‘posix’ os.getcwd() #显示当前目录 os.listdir() #以 ...
- Swift 控制语句
Control Flow指的是『逻辑控制语句』,包括if.if-else.for.for-in.while.do-while.switch等.其中大部分和OC中差不多,但Swift在某些方便增强了控制 ...
- CSS:CSS 网络安全字体组合
ylbtech-CSS:CSS 网络安全字体组合 1.返回顶部 1. 常用的字体组合 font-family 属性应该使用若干种字体名称作为回退系统,以确保浏览器/操作系统之间的最大兼容性.如果浏览器 ...
- bzoj1319
数论 这个幂指数很难搞,那么我们取个log 去取log得有底数,那么自然这个底数能表示出所有的数 原根满足这个性质 那么我们求出原根,再去log 变成k*ind(x)=ind(a) (mod phi( ...