P2408 不同子串个数

题意:

给你一个长为 n 的字符串,求不同的子串的个数。

我们定义两个子串不同,当且仅当有这两个子串长度不一样或者长度一样且有任意一位不一样。

子串的定义:原字符串中连续的一段字符组成的字符串。

题解:

对于任何一个字串,一定是一个后缀的前缀,所以我们可以求出用后缀数组求出LCP(最长公共前缀),再求出每个后缀对答案的贡献。
一个长度为n的字符串,产生的字符串的个数是n(n+1)/2个,这是总数
现在我们开始考虑重复的情况:

后缀sa[i-1]:aaabbdbs
后缀 sa[i]:aabbdbs

height[i]就是2,最前面两个aa是都有的,是重复的,说明这个重复个数正好就是height[i]
但是后面重复情况(bbdbs部分是重复)呀?但其实没关系,因为我们处理了所有的后缀,总会有一个情况处理了bbdbs这情况。这样我们求出整个串中重复的串的个数,就是∑i=1nheight[i]\sum_{i=1}^{n}height[i]∑i=1n​height[i]
答案就是:n(n+1)/2-∑i=1nheight[i]\sum_{i=1}^{n}height[i]∑i=1n​height[i]
记得开ll

代码:

// Problem: P2408 不同子串个数
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2408
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Data:2021-08-22 12:24:46
// By Jozky#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...);
}
template <typename T> inline void write(T x)
{if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0');
}
void rd_test()
{#ifdef LOCALstartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{#ifdef LOCALendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int MAXN= 1000005;char ch[MAXN], all[MAXN];
int sa[MAXN], rk[MAXN], height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;
char str[MAXN];
//rk[i] 第i个后缀的排名; sa[i] 排名为i的后缀位置; height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] rk的辅助数组(计数排序中的第二关键字),与sa意义一样。
//a为原串
void RSort()
{//rk第一关键字,tp第二关键字。for (int i= 0; i <= m; i++)tax[i]= 0;for (int i= 1; i <= n; i++)tax[rk[tp[i]]]++;for (int i= 1; i <= m; i++)tax[i]+= tax[i - 1];for (int i= n; i >= 1; i--)sa[tax[rk[tp[i]]]--]= tp[i]; //确保满足第一关键字的同时,再满足第二关键字的要求
} //计数排序,把新的二元组排序。int cmp(int* f, int x, int y, int w)
{return f[x] == f[y] && f[x + w] == f[y + w];
}
//通过二元组两个下标的比较,确定两个子串是否相同void Suffix()
{//safor (int i= 1; i <= n; i++)rk[i]= a[i], tp[i]= i;m= 127, RSort(); //一开始是以单个字符为单位,所以(m = 127)for (int w= 1, p= 1, i; p < n; w+= w, m= p) { //把子串长度翻倍,更新rk//w 当前一个子串的长度; m 当前离散后的排名种类数//当前的tp(第二关键字)可直接由上一次的sa的得到for (p= 0, i= n - w + 1; i <= n; i++)tp[++p]= i; //长度越界,第二关键字为0for (i= 1; i <= n; i++)if (sa[i] > w)tp[++p]= sa[i] - w;//更新sa值,并用tp暂时存下上一轮的rk(用于cmp比较)RSort(), swap(rk, tp), rk[sa[1]]= p= 1;//用已经完成的sa来更新与它互逆的rk,并离散rkfor (i= 2; i <= n; i++)rk[sa[i]]= cmp(tp, sa[i], sa[i - 1], w) ? p : ++p;}//离散:把相等的字符串的rk设为相同。//LCPint j, k= 0;for (int i= 1; i <= n; height[rk[i++]]= k)for (k= k ? k - 1 : k, j= sa[rk[i] - 1]; a[i + k] == a[j + k]; ++k);//这个知道原理后就比较好理解程序
}void Init()
{int t;read(t);scanf("%s", str);// cout << str << endl;n= strlen(str);for (int i= 0; i < n; i++)a[i + 1]= str[i];
}
int main()
{//rd_test();Init();Suffix();ll ans= 0;// for (int i= 1; i <= n; i++)// cout << "hei[]=" << height[i] << endl;for (int i= 1; i <= n; i++) {ans+= 1ll * (n - i + 1) - 1ll * (height[sa[i]]);}cout << ans;// for (int i= 1; i <= n; i++) {// cout << "rank[i]=" << rk[i] << endl;// }// for (int i= 1; i <= n; i++) {// cout << "hei[i]=" << height[rk[i]] << endl;// }/*aabaaabaabaaaaaaaaaabaaabaabaa*///Time_test();
}

P2408 不同子串个数相关推荐

  1. P2408 不同子串个数 SAM

    传送门 文章目录 题意: 思路: 题意: 求sss中本质不同的字串个数. 1≤∣s∣≤1e51\le |s|\le 1e51≤∣s∣≤1e5 思路: 显然, 个数就是∑(len[i]−len[fa[i ...

  2. 洛谷 P2408 不同子串个数 题解

    一道很好的后缀数组和后缀自动机的练习题.本题解分成两个部分,分别讲解用后缀数组做和用后缀自动机做. 1 1 1.后缀数组 我们知道,后缀数组中的 h e i g h t height height 数 ...

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

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

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

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

  5. [SDOI2016] 生成魔咒(后缀数组SA + st表 + set)动态不同子串个数

    problem luogu-P4070 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1,21,21,2 拼凑起来形成一个魔咒串 [1,2][1,2][1,2]. 一个魔咒串 ...

  6. 【蓝桥杯】算法提高 着急的WYF(不同子串个数)

    算法提高 着急的WYF 问题描述 WYF在战网上的密码非常复杂(含大小写字母.数字以及特殊字符,如"!","@","{"等),但他很不巧地忘 ...

  7. 蓝桥杯 ADV-343 着急的WYF(不同子串个数)

    试题 算法提高 着急的WYF(不同子串个数) 资源限制 时间限制:476ms 内存限制:256.0MB 问题描述 由于战网的密码是一串乱码,WYF巧妙地忘记了他的密码.(他就是作死,如同自掘坟墓.说到 ...

  8. 字符串:字符串顺序比较、11-2删除重复元素、字符串生成器、着急的WYF(不同子串个数)

    字符串顺序比较 问题描述比较两个字符串s1和s2,输出:0表示s1与s2相等:1表示s1的字母序先于s2:-1表示s1的字母序后于s2输入格式输入两行,第一行输入一个字符串1,第二行输入字符串2.输出 ...

  9. 洛谷 - P6292 区间本质不同子串个数(SAM+LCT+线段树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的字符串,再给出 m 次询问,每次询问需要回答区间 [ l , r ] 内有多少个本质不同的字符串 题目分析:首先简化模型,回顾一下如何求解 &quo ...

最新文章

  1. P1051 谁拿了最多奖学金
  2. 用 TS + Vue 写了一个在 Chrome 中运行 Prettier 格式化的扩展程序
  3. MVC 之 属性详解
  4. POJ 1426 Find The Multiple BFS
  5. About Wisdom
  6. ADT版本不同导致的一个问题
  7. [LeetCode] Search in Rotated Sorted Array [35]
  8. express获取参数的几种方法
  9. linux调度器(八)——实时调度与SMP
  10. python turtle原点位置_python中turtle库中setworldcoordinates(坐标系的移动)
  11. 计算机网络面试题总结
  12. qrcode.js的识别解析二维码图片和生成二维码图片
  13. 统信自带浏览器js不兼容问题
  14. 删除磁盘管理中的OEM分区
  15. ‘primordial is not defined‘ node 报错解决方法 终极篇!!
  16. java jnlp_使用Java 7启动JNLP时的NoSuchAlgorithmException
  17. 爱心跳动-HTML-抖音火的那个
  18. sku设计mysql_SKU数据库设计
  19. 我的世界服务器如何修改天气,我的世界天气修改设置、天气关闭锁定、天气指令代码作弊码大全-66街机网...
  20. 如何使用Things3创建重复的待办事项?

热门文章

  1. 深度学习框架PyTorch与TensorFlow,谁更胜一筹?
  2. 这个被称为20世纪最伟大人物的最强理科生,到底有多强,你根本不了解
  3. “杀”一个程序员不需要用枪,改三次需求就可以了!
  4. 因为加班,谈了7年的女友跟我分手了……
  5. 高速的二舍八入三七作五_有没有发现,高速收费都是5的倍数,这是为什么?怎么判断的?...
  6. linux挂载4t硬盘用不了,centos7挂载新加4T硬盘到/home目录
  7. a标签传参接收_[pyecharts1.8] 系列配置之标签设置
  8. css-6 df15,webpack 样式文件的代码分割(15)
  9. 教之初计算机考试函数应用题,教之初计算机考试系统
  10. oracle dbfile数,通过案例学调优之--Oracle参数(db_file_multiblock_read_count)