题意:给出区间,询问区间里不同子串个数。

题解:后缀自动机或者后缀数组
参考求解整个字符串的做法。
用kuangbin的话简单介绍一下SAM

从起点到每个点的不同路径,就是不同的子串。
到每一个点,不同路径,其实就是以这个点为最后一个字符的后缀,长度是介于(p->fa->len,p->len]之间的,个数也就清楚了。
而且这个其实是动态变化的,每加入一个字符,就可以知道新加了几个不同子串。

这里处理的时候加个pos,记录位置,这样就很容易预处理了。
SAM做法:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int CHAR = 26;
const int MAXN = 2020;
struct SAM_Node{SAM_Node* fa, * next[CHAR];int len;int id, pos;SAM_Node() {}SAM_Node(int _len){fa = 0;len = _len;memset(next, 0, sizeof(next));}
};
SAM_Node SAM_node[MAXN * 2], * SAM_root, * SAM_last;
int SAM_size;
SAM_Node* newSAM_Node(int len){SAM_node[SAM_size] = SAM_Node(len);SAM_node[SAM_size].id = SAM_size;return &SAM_node[SAM_size++];
}
SAM_Node* newSAM_Node(SAM_Node* p){SAM_node[SAM_size] = *p;SAM_node[SAM_size].id = SAM_size;return &SAM_node[SAM_size++];
}
void SAM_init(){SAM_size = 0;SAM_root = SAM_last = newSAM_Node(0);SAM_node[0].pos = 0;
}
void SAM_add(int x, int len){  //len从1开始SAM_Node* p = SAM_last, * np = newSAM_Node(p->len + 1);np->pos = len;SAM_last = np;for (; p && !p->next[x]; p = p->fa)p->next[x] = np;if (!p){np->fa = SAM_root;return;}SAM_Node* q = p->next[x];if (q->len == p->len + 1){np->fa = q;return;}SAM_Node* nq = newSAM_Node(q);nq->len = p->len + 1;q->fa = nq;np->fa = nq;for (; p && p->next[x] == q; p = p->fa) p->next[x] = nq;
}
void SAM_build(char* s){SAM_init();int len = strlen(s);for (int i = 0; i < len; i++) SAM_add(s[i] - 'a', i + 1);
}int Q[MAXN][MAXN];
char s[MAXN];
int t, q, l, r;
int main() {scanf("%d", &t);while (t--) {scanf("%s", s);int len = strlen(s);memset(Q, 0, sizeof(Q));for (int i = 0; i < len; i++) {SAM_init();      //建立len个samfor (int j = i; j < len; j++) {SAM_add(s[j] - 'a', j - i + 1);}for (int j = 1; j < SAM_size; j++) {  //算出每个点 注意最后一个节点无parentQ[i][SAM_node[j].pos + i - 1] += SAM_node[j].len - SAM_node[j].fa->len;}for (int j = i + 1; j < len; j++) {  //累加Q[i][j] += Q[i][j - 1];}}scanf("%d", &q);while (q--) {scanf("%d%d", &l, &r);printf("%d\n", Q[--l][--r]);}}return 0;
}

SA做法:
参考height,因为是区间,所以用pre构造新的排名。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int MAXN = 20010;
int t1[MAXN], t2[MAXN], c[MAXN];//求 SA 数组需要的中间变量,不需要赋值//待排序的字符串放在 s 数组中,从 s[0] 到 s[n-1], 长度为 n, 且最大值小于 m,//除 s[n-1] 外的所有 s[i] 都大于 0, r[n-1]=0//函数结束以后结果放在 sa 数组中
bool cmp(int* r, int a, int b, int l) {return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int str[], int sa[], int rank[], int height[], int n, int m) {n++;int i, j, p, * x = t1, * y = t2;//第一轮基数排序,如果 s 的最大值很大,可改为快速排序for (i = 0; i < m; i++)c[i] = 0;for (i = 0; i < n; i++)c[x[i] = str[i]]++;for (i = 1; i < m; i++)c[i] += c[i - 1];for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;for (j = 1; j <= n; j <<= 1) {p = 0;//直接利用 sa 数组排序第二关键字for (i = n - j; i < n; i++)y[p++] = i;//后面的 j 个数第二关键字为空的最小for (i = 0; i < n; i++)if (sa[i] >= j)y[p++] = sa[i] - j;//这样数组 y 保存的就是按照第二关键字排序的结果//基数排序第一关键字for (i = 0; i < m; i++)c[i] = 0;for (i = 0; i < n; i++)c[x[y[i]]]++;for (i = 1; i < m; i++)c[i] += c[i - 1];for (i = n - 1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i];//根据 sa 和 x 数组计算新的 x 数组swap(x, y);p = 1; x[sa[0]] = 0;for (i = 1; i < n; i++)x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;if (p >= n)break;m = p;//下次基数排序的最大值}int k = 0;n--;for (i = 0; i <= n; i++)rank[sa[i]] = i;for (i = 0; i < n; i++) {if (k)k--;j = sa[rank[i] - 1];while (str[i + k] == str[j + k])k++;height[rank[i]] = k;}
}
int Rank[MAXN], height[MAXN], sa[MAXN];
int RMQ[MAXN];
int mm[MAXN];
int best[20][MAXN];
void initRMQ(int n) {for (int i = 1; i <= n; i++) RMQ[i] = height[i];mm[0] = -1;for (int i = 1; i <= n; i++)mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];for (int i = 1; i <= n; i++)best[0][i] = i;for (int i = 1; i <= mm[n]; i++)for (int j = 1; j + (1 << i) - 1 <= n; j++) {int a = best[i - 1][j];int b = best[i - 1][j + (1 << (i - 1))];if (RMQ[a] < RMQ[b])best[i][j] = a;else best[i][j] = b;}
}
int askRMQ(int a, int b) {int t;t = mm[b - a + 1];b -= (1 << t) - 1;a = best[t][a]; b = best[t][b];return RMQ[a] < RMQ[b] ? a : b;
}
int lcp(int a, int b) {a = Rank[a]; b = Rank[b];if (a > b)swap(a, b);return height[askRMQ(a + 1, b)];
}
char str[MAXN];
int r[MAXN], q, L, R, t;
int main() {scanf("%d", &t);while (t--) {scanf("%s", str);int len = strlen(str);for (int i = 0; i < len; i++) r[i] = str[i];r[len] = 0;da(r, sa, Rank, height, len, 128);initRMQ(len);scanf("%d", &q);while (q--) {scanf("%d%d", &L, &R);L--, R--;int cnt = R - L + 1, ans = cnt * (cnt + 1) / 2, pre = -1;for (int i = 1; i <= len; i++) {if (!cnt)break;if (sa[i] < L || sa[i] > R) continue;cnt--;if (pre == -1) {pre = i;continue;}int temp = lcp(sa[pre], sa[i]);int la = R - sa[pre] + 1, lb = R - sa[i] + 1;if (la < lb || temp < lb) pre = i;ans -= min(temp, min(la, lb));}printf("%d\n", ans);}}return 0;
}

HDU 4622 Reincarnation (后缀数组|后缀自动机)相关推荐

  1. bzoj2946 [Poi2000]公共串(后缀数组 || 后缀自动机)

    bzoj2946 [Poi2000]公共串 原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2946 题意: 给出几个由小写字母构成的单词,求 ...

  2. luoguP5108 仰望半月的夜空 [官方?]题解 后缀数组 / 后缀树 / 后缀自动机 + 线段树 / st表 + 二分...

    仰望半月的夜空 题解 可以的话,支持一下原作吧... 这道题数据很弱..... 因此各种乱搞估计都是能过的.... 算法一 暴力长度然后判断判断,复杂度\(O(n^3)\) 期望得分15分 算法二 通 ...

  3. HDU - 4622 Reincarnation(后缀自动机-查询区间本质不同子串个数)

    题目链接:点击查看 题目大意:给出一个长度为 n 的字符串,再给出 q 个询问,每次询问需要回答区间 [ l , r ] 内有多少个本质不同的子串 题目分析:和回文自动机那个题目一样,n * n 预处 ...

  4. hdu 4622 Reincarnation SAM模板题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给定一个长度不超过2000的字符串,之后有Q次区间查询(Q <= 10000),问区 ...

  5. 五分钟搞懂后缀数组!

    为什么学后缀数组 后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握.  学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些S ...

  6. 五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)

    为什么学后缀数组 后缀数组是一个比较强大的处理字符串的算法,是有关字符串的基础算法,所以必须掌握. 学会后缀自动机(SAM)就不用学后缀数组(SA)了?不,虽然SAM看起来更为强大和全面,但是有些SA ...

  7. 后缀树和后缀数组的一些资料收集

    后缀树(Suffix tree)是一种数据结构,能快速解决很多关于字符串的问题.后缀树的概念最早由Weiner 于1973年提出,既而由McCreight 在1976年和Ukkonen在1992年和1 ...

  8. 后缀数组求最长重复子串

    问题描述 给定一个字符串,求出其最长重复子串 例如:abcdabcd 最长重复子串是 abcd,最长重复子串可以重叠 例如:abcdabcda,这时最长重复子串是 abcda,中间的 a 是被重叠的. ...

  9. 算法竞赛进阶指南——后缀数组

    后缀数组 后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围. 在本题中,我们希望使用快排.Hash与二分实现一个简单的O(nlog2n)的后缀数组求法. ...

  10. 后缀数组(倍增)学习记录,我尽可能详细的讲了

    后缀数组(倍增) 后缀数组 后缀数组能干什么 一些基本概念 那么到底怎么排序呢? 倍增排序 具体执行排序呢? 基数排序 关于排序的桶 关于桶排序在字符串倍增中的嵌入 具体改执行的排序事情 倍增排序的代 ...

最新文章

  1. Laravel Redis操作大全
  2. python 三分钟入门_Cython 三分钟入门教程
  3. 电气期刊论文实现:基于输电线路容量安全约束的电力机组组合
  4. java计算时间差_JAVA并发编程三大Bug源头(可见性、原子性、有序性),彻底弄懂...
  5. python 依据某几列累加求和_关于Python数组求和的四个问题及详解,让你更加爱Python!...
  6. VisualTreeHelper
  7. numpy.squeeze()的用法
  8. mac 电脑安装 svn和brew
  9. 中国34个省级行政区2000年-2021年逐月NDVI统计分析结果
  10. CentOS系统下载阿里yum源
  11. 四大因素使二三线城市楼市泡沫难救?
  12. USYD悉尼大学DATA1002 详细作业解析Module4
  13. 高斯投影坐标计算例题_高斯投影坐标计算程序下载
  14. Windows程式开发设计指南(二十一)动态连结程式库
  15. 论文阅读:Gradient Harmonized Single-stage Detector
  16. weblogic卸载 for linux
  17. win10微软输入法不显示选字框?
  18. 记住沃伦巴菲特这三十条
  19. 【Python机器学习预测分析算法实战三】预测模型性能评估及影响因素
  20. swoft 2启动rpc

热门文章

  1. 中国车牌号的分类说明识别及含义
  2. CyanogenMod源码编译
  3. 5部靠身材和脸蛋撑起了整部电影,女主光环太刺眼,部部是经典!
  4. linux环境变量设置详解
  5. 腾讯云-语音指定模板-电话告警通知
  6. Win10系统开启黑暗主题
  7. MATLAB必看书籍推荐
  8. 《Java编程培训教程》
  9. PCB设计中,如何快速消除PCB布线?
  10. unbalanced calls to begin/end appearance transitions for uiviewcontroller的解决方法