hdu 4622

Problem Description

Now you are back,and have a task to do:
Given you a string s consist of lower-case English letters only,denote f(s) as the number of distinct sub-string of s.
And you have some query,each time you should calculate
f(s[l...r]), s[l...r] means the sub-string of s start from l end at r.

Input

The first line contains integer T(1<=T<=5), denote the number of the test cases.
For each test cases,the first line contains a string s(1 <= length of s <= 2000).
Denote the length of s by n.
The second line contains an integer Q(1 <= Q <= 10000),denote the number of queries.
Then Q lines follows,each lines contains two integer l, r(1 <= l <= r <= n), denote a query.

Output

For each test cases,for each query,print the answer in one line.

Sample Input

2

bbaba

5

3 4

2 2

2 5

2 4

1 4

baaba

5

3 3

3 4

1 4

3 5

5 5

Sample Output

3

1

7

5

8

1

3

8

5

1

题意:

给出一个字符串,求出在每个区间内的字串的个数....

思路:

两个做法,SAM 和 SA.

先说SAM. SAM的一个非常实用的性质,SAM是在线增量构造的.所以,我们就能够在线构造每个区间所对应的SAM.

合并左端点相同的询问,然后在线构造SAM,构造出SAM后,用O(n)的时间内统计出字串就ok了. 跟SPOJ那道题方法差不多.

然后是SA

参考的沐阳的博客....写得非常有道理.

构造出SA之后,我们通过遍历在局部的后缀(起点在[L,R]之间)来计算字串的贡献.....

关键是我们如何通过全局的SA来推算局部的SA.

la 与 lb 分别为一前一后的后缀在区间内的长度.

如果 lcp < la && lcp < lb

那么 la 就是下一个后缀.

如果 lcp >= la && lcp >= lb

如果 la < lb 就要换后缀.

如果 lcp >= la && lcp < lb

就更换后缀.

这样下来我们就能够得出全局的SA

然后在中间维护一个rmq就能够快速求出lcp

问题就解决了.

 1 #include<cstdlib>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define rep(i,a,b) for(i = a; i < b; ++i)
 6 #define rev(i,a,b) for(i = a; i >= b; --i)
 7 using namespace std;
 8 const int maxn = (int)2500, inf = 0x3f3f3f3f;
 9 int sa[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],c[maxn];
10 char str[maxn];
11 int n;
12 int cmp(int x[],int a,int b,int c){
13     return x[a] == x[b] && x[a+c] == x[b+c];
14 }
15 void build(int m){
16     int i,j,p = 0, *x = t1, *y = t2;
17     rep(i,0,m) c[i] = 0;
18     rep(i,0,n) c[x[i] = str[i]]++;
19     rep(i,1,m) c[i] += c[i-1];
20     rev(i,n-1,0) sa[--c[x[i]]] = i;
21     for(j = 1; j < n && p < n; j <<= 1, m = p){
22         p = 0;
23         rep(i,n-j,n) y[p++] = i;
24         rep(i,0,n) if(sa[i] >= j) y[p++] = sa[i] - j;
25         rep(i,0,m) c[i] = 0;
26         rep(i,0,n) c[x[y[i]]]++;
27         rep(i,1,m) c[i] += c[i-1];
28         rev(i,n-1,0) sa[--c[x[y[i]]]] = y[i];
29         for(swap(x,y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
30             x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p - 1 : p++;
31     }
32 }
33 void calc_height(){
34     int i,j,k = 0;
35     rep(i,0,n) rank[sa[i]] = i;
36     for(i = 0; i < n; height[rank[i++]] = k)
37         for(k ? k-- : 0, j = sa[rank[i] - 1]; str[i+k] == str[j+k] && min(i,j) + k < n; k++);
38 }
39 int l,r,lcp,last,ret;
40 int getlen(int st,int ed){
41     return ed - st + 1;
42 }
43 int calc(int now,int pas){
44     int la = getlen(sa[now],r), lb = getlen(sa[pas],r);
45     return pas ? la - min(lcp,min(lb,la)) : la;
46 }
47 int getnext(int now,int pas){
48     if(!pas) return now;
49     int la = getlen(sa[now],r), lb = getlen(sa[pas],r);
50     if(lcp < min(la,lb)) return now;
51     if(lcp >= la && lcp >= lb && la > lb) return now;
52     if(lcp < la && lcp >= lb) return now;
53     return pas;
54 }
55 void solve(){
56     int i;
57     ret = last = 0; lcp = inf;
58     rep(i,0,n){
59         if(l <= sa[i] && sa[i] <= r){
60             if(last) lcp = min(lcp,height[i]);
61             ret += calc(i,last);
62             int t = getnext(i,last);
63             if(t == i) lcp = getlen(sa[i],r), last = i;
64         }
65         else if(last) lcp = min(lcp, height[i]);
66     }
67     printf("%d\n",ret);
68 }
69 int main()
70 {
71     freopen("rec.in","r",stdin);
72     freopen("rec.out","w",stdout);
73     int T,q;
74     scanf("%d\n",&T);
75     while(T--){
76         scanf("%s",str);
77         n = strlen(str);
78         str[n++] = ‘a‘ - 1;
79         build(255);
80         calc_height();
81         scanf("%d\n",&q);
82         while(q--){
83             scanf("%d %d\n",&l,&r);
84             l--; r--;
85             solve();
86         }
87     }
88     return 0;
89 }

时间: 2024-10-12 22:20:30

hdu 4622的相关文章

HDU 4622 Reincarnation Hash解法详解

今天想学字符串hash是怎么弄的.就看到了这题模板题 http://acm.hdu.edu.cn/showproblem.php?pid=4622 刚开始当然不懂啦,然后就上网搜解法.很多都是什么后缀自动机那些.作为小白的我当然不懂啦,更重要的是我想学的是字符串hash这种解法呢?然而有这种解法,但是却都是只有代码,看起来很辛苦.所以这里我把我的理解写上来,当然有错误的话,请各路高手指出来,我也好好学习下~~ 首先介绍一个字符串Hash的优秀映射函数:BKDRHash,这里hash一开始是等于0

HDU 4622 Reincarnation

Reincarnation Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on HDU. Original ID: 462264-bit integer IO format: %I64d      Java class name: Main Now you are back,and have a task to do:Given you a string s consist of lower-case E

HDU 4622 Reincarnation(后缀自动机)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4622 [题目大意] 给出一个长度不超过2000的字符串,有不超过10000个询问,问[L,R]子串中出现的子串数目,相同子串不可重复计数. [题解] 考虑到字符串长度只有两千,我们对每个位置往后建立2000个后缀自动机, 这样子就能分别计算每个位置往后出现的字符串数目并保存, 对于sam上的一个节点来说,它的匹配长度与失配位置的匹配长度只差就是他们之间的子串, 所以,我们在建立sam可以同时计算

hdu 4622 Reincarnation SAM模板题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给定一个长度不超过2000的字符串,之后有Q次区间查询(Q <= 10000),问区间中不同的子串数为多少? 学习资料: 知乎 SAM解析: 看了clj的PPT,对于最后为什么这样子插入节点还是有些不懂... 下面先讲讲一些理解 1.找出母串A中的所有子串可以看做是先找出A串的所有后缀,再在后缀中找出前缀(后缀中找前缀):其中的init(初始状态)是指可以匹配所有后缀的原始状态,即可以对

HDU 4622 Reincarnation(SAM)

Problem Description Now you are back,and have a task to do:Given you a string s consist of lower-case English letters only,denote f(s) as the number of distinct sub-string of s.And you have some query,each time you should calculate f(s[l...r]), s[l..

hdu 4622 Reincarnation(后缀数组|后缀自动机|KMP)

Reincarnation Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 2138    Accepted Submission(s): 732 Problem Description Now you are back,and have a task to do: Given you a string s consist of lo

HDU 4622 求解区间字符串中的不同子串的个数

题目大意: 给定一个长度<2000的串,再给最多可达10000的询问区间,求解区间字符串中的不同子串的个数 这里先考虑求解一整个字符串的所有不同子串的方法 对于后缀自动机来说,我们动态往里添加一个字符,每次添加一个字符进去,我们只考虑那个生成的长度为当前长度的后缀自动机的节点 那么这个节点可接收的字符串的个数就是( p->l - p->f->l ),也就是以当前点为最后节点所能得到的与之前不重复的子串的个数 那么这个问题就很好解决了,共2000个位置,以每一个位置为起点构建一次后缀

HDU 4622 Reincarnation( 任意区间子串的长度, 后缀数组+RMQ)

题目大意:给你一个字符串,给你N次查询,每次给你一个区间让你求出这个区间里面有多少子串. 解题思路:我们肯定要枚举位置,然后找公共子串然后再去掉重复的,但是他的地址对应的rank不是连续的,如果暴力找的话会n*n会超时. 从这个博客学习到一种方法:首先对整个字符串求一次sa[]以及height[],之后对于任意区间[L, R],遍历一遍sa[],只要起点在[L, R]内的后缀就需要进行统计,类似于1)中的方法,不过有一个地方要特别注意的就是全部的sa[]不一定就是区间内的sa[],这是因为区间内

字符串(后缀自动机):HDU 4622 Reincarnation

Reincarnation Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 3194    Accepted Submission(s): 1184 Problem Description Now you are back,and have a task to do: Given you a string s consist of lo