Given a string, we need to find the total number of its distinct substrings.

Input

\(T-\) number of test cases. \(T<=20\);

Each test case consists of one string, whose length is \(<=1000\)

Output

For each test case output one number saying the number of distinct substrings.

Sample Input

2
CCCCC
ABABA

Sample Output

5
9

题意:

给出\(n\)个串,求每个串中本质不同的子串

题解:

一、后缀自动机

把串前一个后缀自动机,然后在每次加入字符的时候把答案加上当前长度和他\(parent\)的点的长度的差。这里利用了后缀自动机的一个性质:

  • 每个点后面的本质不同的串的个数等于这个点的长度减去他的\(parent\)的长度。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
char s[N];
int a[N],c[N],as;
struct SAM{
int last,cnt;
int size[N],ch[N][52],fa[N<<1],l[N<<1];
void ins(int c){
int p=last,np=++cnt;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[p]+1==l[q])fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
as+=l[np]-l[fa[np]];
}
void build(char s[]){
memset(ch,0,sizeof ch);
memset(l,0,sizeof l);
memset(fa,0,sizeof fa);
memset(size,0,sizeof size);
int len=strlen(s+1);
last=cnt=1;
for(int i=1;i<=len;++i){
if('A'<=s[i]&&s[i]<='Z')ins(s[i]-'A');
else ins(s[i]-'a'+26);
}
}
}sam;
int main(){
int n;
cin>>n;
while(n--){
as=0;
scanf("%s",s+1);
sam.build(s);
printf("%d\n",as);
}
}

二、后缀数组

处理出sa和height,以公式 当前后缀的贡献%c[i]=n-sa[i]+1-height[i]$计算出结果就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char s[N];
int n;
int fir[N],sec[N],rnk[N],t[N],sa[N],b[N];
void sort(){
memset(t,0,sizeof t);
for(int i=1;i<=n;++i)t[sec[i]]++;
for(int i=1;i<N;++i)t[i]+=t[i-1];
for(int i=n;i;--i)b[t[sec[i]]--]=i;
memset(t,0,sizeof t);
for(int i=1;i<=n;++i)t[fir[b[i]]]++;
for(int i=1;i<N;++i)t[i]+=t[i-1];
for(int i=n;i;--i)sa[t[fir[b[i]]]--]=b[i];
}
int height[N];
void get_height(char *s){
int k=0;
for(int i=1;i<=n;++i){
if(rnk[i]==1){
height[i]=0;
continue;
}
if(k)--k;
int j=sa[rnk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[i]=k;
}
}
void get_sa(char *s){
for(int i=1;i<=n;++i)rnk[i]=s[i];
for(int k=1;k<=n;k*=2){
for(int i=1;i<=n;++i){
fir[i]=rnk[i];
if(i+k>n)sec[i]=0;
else sec[i]=rnk[i+k];
}
sort();
int num=1;rnk[sa[1]]=1;
for(int i=2;i<=n;++i){
if(fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]])num++;
rnk[sa[i]]=num;
}
if(num==n)break;
}
}
int main(){
int t;
cin>>t;
while(t--){
scanf("%s",s+1);
n=strlen(s+1);
get_sa(s);
get_height(s);
long long ans=0;
for(int i=1;i<=n;++i){
ans+=n-sa[i]-height[i]+1;
}
printf("%lld\n",ans);
}
}

Distinct Substrings(spoj694)(sam(后缀自动机)||sa(后缀数组))的更多相关文章

  1. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  2. 【Luogu3804】【模板】后缀自动机(后缀自动机)

    [Luogu3804][模板]后缀自动机(后缀自动机) 题面 洛谷 题解 一个串的出现次数等于\(right/endpos\)集合的大小 而这个集合的大小等于所有\(parent\)树上儿子的大小 这 ...

  3. D. Match & Catch 后缀自动机 || 广义后缀自动机

    http://codeforces.com/contest/427/problem/D 题目是找出两个串的最短公共子串,并且在两个串中出现的次数只能是1次. 正解好像是dp啥的,但是用sam可以方便很 ...

  4. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数

    目录 求若干个串的公共子串个数相关变形题 对一个串建后缀自动机,另一个串在上面跑同时计数 广义后缀自动机 后缀数组 其他:POJ 3415 求两个串长度至少为k的公共子串数量 @(牛客多校第四场 I题 ...

  5. [bzoj4199][Noi2015]品酒大会_后缀自动机_后缀树_树形dp

    品酒大会 bzoj-4199 Noi-2015 题目大意:给定一个字符串,如果其两个子串的前$r$个字符相等,那么称这两个子串的开头两个位置$r$相似.如果两个位置勾兑在一起那么美味度为两个位置的乘积 ...

  6. spoj SUBST1 - New Distinct Substrings【SAM||SA】

    SAM里的转台不会有重复串,所以答案就是每个right集合所代表的串个数的和 #include<iostream> #include<cstdio> #include<c ...

  7. Luogu3804 【模板】后缀自动机(后缀自动机)

    建出parent树统计即可.开始memcpy处写的是sizeof(son[y]),然后就T掉了……还是少用这种东西吧. 同时也有SA做法.答案子串一定是名次数组中相邻两个串的lcp.单调栈统计其是几个 ...

  8. luogu SP8093 后缀自动机+树状数组+dfs序

    这题解法很多,简单说几个: 1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的. 2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力. 3. 建立后缀树后在 $ ...

  9. BZOJ 2780 Sevenk Love Oimaster (后缀自动机+树状数组+dfs序+离线)

    题目大意: 给你$n$个大串和$m$个询问,每次给出一个字符串$s$询问在多少个大串中出现过 好神的一道题 对$n$个大串建出广义$SAM$,建出$parent$树 把字符串$s$放到$SAM$里跑, ...

随机推荐

  1. POJ-3252 Avenger

    题意:在区间中,他们化成2进制的数的0的个数大于等于1的数有多少个. 思路:我们需要记录上一次0和1的个数,此外我们还要特别注意一下前导0. 如果前面全是0的时候我们就要注意下一位是不是还是0,如果一 ...

  2. rails 辅助方法

    rails辅助方法全解: https://ruby-china.github.io/rails-guides/routing.html

  3. python中的函数嵌套

    一.函数嵌套 1.只要遇到了()就是函数的调用.如果没有就不是函数的调用 2.函数的执行顺序 遵循空间作用域,遇到调用才执行 def outer(): def inner(): print(" ...

  4. liunx基础命令

    linux的简单介绍 linux是一款免费使用和自由传播的内似于unix的操作系统软件,是一个基于POSI和unix的多用户,多任务,支持多线程和多CPU的一种操作系统.主要用于服务器,特别是网络服务 ...

  5. 2019.01.02 bzoj2467: [中山市选2010]生成树(矩阵树定理)

    传送门 矩阵树定理模板题. 题意简述:自己看题面吧太简单懒得写了 直接构建出这4n4n4n个点然后按照题面连边之后跑矩阵树即可. 代码: #include<bits/stdc++.h> # ...

  6. 2018.11.06 洛谷P1941 飞扬的小鸟(背包)

    传送门 上升看成完全背包. 下降看成01背包. 注意边界转移就行了. 代码: #include<bits/stdc++.h> using namespace std; inline int ...

  7. mysql学习之路_连接查询

    回顾 列属性:主键,自增长,唯一键. 关系:一对一,一对多,多对多 三层范式: 1NF:字段设计必须符合原子性 2NF:不存在部分依赖(没有复合主键) 3NF:不存在传递依赖(实体单独成表) 逆规范化 ...

  8. js短信验证码

    短信验证码,无注释,url顺便写的错的,所以会报错 <!DOCTYPE html> <html> <head> <meta charset="UTF ...

  9. 利用tcpcopy引流过程

    tcpcopy是一个tcp流量复制工具,当前还支持udp和mysql流量的复制. 目的: 将机器10.24.110.21的5000端口流量引流到机器10.23.25.11的5000端口. 示例:将10 ...

  10. web-day16

    第16章WEB16-Listener&Filter篇 今日任务 使用过滤器完成自动登录的案例 使用过滤器统一网站的字符集编码 教学导航 教学目标 了解常见的监听器 理解过滤器的生命周期 能够使 ...