传送门

解法一:后缀数组

可以知道每一个子串都是后缀的前缀,那么对于第\(i\)小的后缀的贡献就可以表示为n-sa[i]+1

然而会存在重复的子串,注意height数组的定义,对于sa[i-1]和sa[i],只有height[i]个子串会被重复计算,每次都减掉就好了

代码:

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. using namespace std;
  5. void read(int &x) {
  6. char ch; bool ok;
  7. for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
  8. for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
  9. }
  10. #define rg register
  11. const int maxn=1e5+10;long long ans;
  12. int n,a[maxn],m='z',x[maxn],y[maxn],num,sa[maxn],rk[maxn],h[maxn];char p[maxn];
  13. int main()
  14. {
  15. read(n);scanf("%s",p+1);
  16. for(rg int i=1;i<=n;i++)a[x[i]=p[i]]++;
  17. for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
  18. for(rg int i=n;i;i--)sa[a[x[i]]--]=i;
  19. for(rg int k=1;k<=n;k<<=1,num=0)
  20. {
  21. for(rg int i=n-k+1;i<=n;i++)y[++num]=i;
  22. for(rg int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
  23. for(rg int i=1;i<=m;i++)a[i]=0;
  24. for(rg int i=1;i<=n;i++)a[x[i]]++;
  25. for(rg int i=1;i<=m;i++)a[i]+=a[i-1];
  26. for(rg int i=n;i;i--)sa[a[x[y[i]]]--]=y[i];
  27. for(rg int i=1;i<=n;i++)y[i]=x[i];
  28. num=x[sa[1]]=1;
  29. for(rg int i=2;i<=n;i++)
  30. if(y[sa[i]]!=y[sa[i-1]]||y[sa[i]+k]!=y[sa[i-1]+k])x[sa[i]]=++num;
  31. else x[sa[i]]=num;
  32. if(num>=n)break;m=num;
  33. }
  34. for(rg int i=1;i<=n;i++)rk[sa[i]]=i;
  35. for(rg int i=1,k=0,j;i<=n;h[rk[i++]]=k)
  36. for(k=k?k-1:k,j=sa[rk[i]-1];p[j+k]==p[i+k];k++);
  37. for(rg int i=1;i<=n;i++)ans+=n-sa[i]-h[i]+1;printf("%lld\n",ans);
  38. }

解法二:后缀自动机

建出后缀自动机,拓扑排序就行了

代码:

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<queue>
  6. using namespace std;
  7. void read(int &x){
  8. char ch; bool ok;
  9. for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
  10. for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
  11. }
  12. #define rg register
  13. const int maxn=5e6+10;
  14. int n,tot,las,pre[maxn],nxt[maxn],h[maxn],cnt,in[maxn];
  15. char a[maxn];
  16. long long ans,f[maxn];
  17. struct sam{int len,link,ch[26];}s[maxn];
  18. void sam_pre(){s[0].len=0,s[0].link=-1;}
  19. void ins(int x){
  20. int cur=++tot,p=las;s[cur].len=s[p].len+1;
  21. while(p!=-1&&!s[p].ch[x])s[p].ch[x]=cur,p=s[p].link;
  22. if(p==-1)s[cur].link=0;
  23. else{
  24. int q=s[p].ch[x];
  25. if(s[q].len==s[p].len+1)s[cur].link=q;
  26. else{
  27. int now=++tot;s[now].len=s[p].len+1;
  28. s[now].link=s[q].link;
  29. memcpy(s[now].ch,s[q].ch,sizeof s[q].ch);
  30. while(p!=-1&&s[p].ch[x]==q)s[p].ch[x]=now,p=s[p].link;
  31. s[q].link=s[cur].link=now;
  32. }
  33. }
  34. las=cur;
  35. }
  36. void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
  37. void top_sort(){
  38. queue<int>q;
  39. for(rg int i=0;i<=tot;i++)if(!in[i])q.push(i);
  40. while(!q.empty()){
  41. int x=q.front();q.pop();
  42. ans+=f[x];
  43. for(rg int i=h[x];i;i=nxt[i]){
  44. f[pre[i]]+=f[x];
  45. if(!(--in[pre[i]]))q.push(pre[i]);
  46. }
  47. }
  48. }
  49. int main(){
  50. read(n),scanf("%s",a+1),sam_pre();
  51. for(rg int i=1;i<=n;i++)ins(a[i]-'a');
  52. for(rg int i=0;i<=tot;i++)
  53. for(rg int j=0;j<26;j++)
  54. if(s[i].ch[j])add(i,s[i].ch[j]),in[s[i].ch[j]]++;
  55. f[0]=1,top_sort(),printf("%lld\n",ans-1);
  56. }

luoguP2408不同子串个数的更多相关文章

  1. [TyvjP1515] 子串统计 [luoguP2408] 不同子串个数(后缀数组)

    Tyvj传送门 luogu传送门 经典题 统计一个字符串中不同子串的个数 一个字符串中的所有子串就是所有后缀的前缀 先求出后缀数组,求出后缀数组中相邻两后缀的 lcp 那么按照后缀数组中的顺序遍历求解 ...

  2. HDU 4622 Reincarnation (查询一段字符串的不同子串个数,后缀自动机)

    Reincarnation Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)To ...

  3. HDU 3948 不同回文子串个数

    集训队论文中有求不同子串个数的做法,就是扫一遍height数组,过程中根据height数组进行去重.对于本题也是雷同的,只是每一次不是根据与排名在上一位的LCP去重,而是与上一次统计对答案有贡献的后缀 ...

  4. HDU4622 (查询一段字符串的不同子串个数,后缀自动机)

    http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给出一个字符串和q次询问,每次询问[l,r]区间内不同子串的个数 分析: N<=2000. 我 ...

  5. [spoj DISUBSTR]后缀数组统计不同子串个数

    题目链接:https://vjudge.net/contest/70655#problem/C 后缀数组的又一神奇应用.不同子串的个数,实际上就是所有后缀的不同前缀的个数. 考虑所有的后缀按照rank ...

  6. ACdream 1430——SETI——————【后缀数组,不重叠重复子串个数】

    SETI Time Limit: 4000/2000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) Submit Statist ...

  7. HDU 5056 Boring count(不超过k个字符的子串个数)

    Boring count Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tot ...

  8. Luogu P2408 不同子串个数【SAM】

    P2408 不同子串个数 计算一个字符串的不同子串个数 两种方法,一种是\(dp\)出来\(SAM\)从起点开始的路径数量 另一种方法就是计算每个点的\(len[i]-len[link[i]]\)这个 ...

  9. hdu5056(找相同字母不出现k次的子串个数)

    题意:      给你一个字符串,然后问你这个字符串里面有多少个满足要求的子串,要求是每个子串相同字母出现的次数不能超过k. 思路:      这种题目做着比较有意思,而且不是很难(但自己还是嘚瑟,w ...

随机推荐

  1. 阿里云Redis开发规范(转)

    一.键值设计 1. key名设计 (1)[建议]: 可读性和可管理性 以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id ugc:video: (2)[建议]:简洁性 ...

  2. Could not get unknown property 'packageForR' for task ':app:processDebugReso

    butterknife 注解框架的问题 删除项目中的有关butterknife 的 apply plgin.classpath 字段 把注解框架改为最新版本 implementation 'com.j ...

  3. codeforces776D

    传送门 这题的意思就是原本有一个长度为n的01串,再给出m的长度为n的01串,要求你判定是否可以通过原串与m个串中的某些串xor使得原串到达一个状态.n,m小于1e5. 这题最初我发现不可做,因为这貌 ...

  4. Myeclipse项目内容没有报错但是项目上面却有红色叉叉

    当src文件夹为空的时候,git是不提交空文件夹的,所以check出来的项目中没有src文件夹,这个时候也会出现此问题.

  5. Ubuntu 16.04 安装wine

    1.安装源       sudo add-apt-repository ppa:wine/wine-builds       sudo apt-get update 2.安装wine      sud ...

  6. 洛谷 1072 Hankson 的趣味题——质因数界限讨论

    题目:https://www.luogu.org/problemnew/show/P1072 思路是把每个数质因数分解,答案对于每个质因数的次数有选择的区间,通过这个计算. 指数的限制就是上限是b1, ...

  7. Azure Key Vault (2) 使用Azure Portal创建和查看Azure Key Vault

    <Windows Azure Platform 系列文章目录> 请注意: 文本仅简单介绍如何在Azure Portal创建和创建Key Vault,如果需要结合Application做二次 ...

  8. Azure SQL Database (27) 创建Table Partition

    <Windows Azure Platform 系列文章目录> 昨天客户正好提到这个问题,现在记录一下. 我们在使用传统的SQL Server,会使用Table Partition,这个功 ...

  9. android开发中怎么通过Log函数输出当前行号和当前函数名

    public class Debug { public static int line(Exception e) { StackTraceElement[] trace = e.getStackTra ...

  10. 在项目中添加全局的 pch 文件

    说明,本片博文仅仅是方便自己以后在添加 pch 文件的配置时候参照使用,担心一些配置的路径由于时间而遗忘. (1)建一个 pch 文件 注意下面要 在 Targets 后打上 对号 (2)对该文件进行 ...