HDU 4622 Reincarnation 后缀自动机

Reincarnation

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

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

Hint

I won‘t do anything against hash because I am nice.Of course this problem has a solution that don‘t rely on hash.

题意:

  给你一个母串,

  Q个询问,每次询问你[L,R] 属于这一段中不同子串的个数是多少

题解:

  考虑离线

  把询问缩小,相同L的询问划分为一类

  这样最多就是建立 2000 个后缀自动机了

#include <bits/stdc++.h>
inline long long read(){long long x=0,f=1;char ch=getchar();while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}return x*f;}
using namespace std;

const int N = 2e3+7;

const long long mod = 1000000007;

long long now;
int isPlus[N * 2],endpos[N * 2];int d[N * 2];
int tot,slink[2*N],trans[2*N][28],minlen[2*N],maxlen[2*N],pre;
int newstate(int _maxlen,int _minlen,int* _trans,int _slink){
    maxlen[++tot]=_maxlen;minlen[tot]=_minlen;
    slink[tot]=_slink;
    if(_trans)for(int i=0;i<26;i++)trans[tot][i]=_trans[i],d[_trans[i]]+=1;
    return tot;
}
long long update(int u) {
    return 1LL*(maxlen[u] - minlen[u] + 1);
}
int add_char(char ch,int u){
    int c=ch-‘a‘,v=u;
    int z=newstate(maxlen[u]+1,-1,NULL,0);
    isPlus[z] = 1;
    while(v&&!trans[v][c]){trans[v][c]=z;d[z]+=1;v=slink[v];}
    if(!v){ minlen[z]=1;slink[z]=1;now += update(z);return z;}
    int x=trans[v][c];
    if(maxlen[v]+1==maxlen[x]){slink[z]=x;minlen[z]=maxlen[x]+1;now += update(z);return z;}
    int y=newstate(maxlen[v]+1,-1,trans[x],slink[x]);
    now -= update(x);
    slink[z]=slink[x]=y;minlen[x]=minlen[z]=maxlen[y]+1;
    now += update(x);
    while(v&&trans[v][c]==x){trans[v][c]=y;d[x]--,d[y]++;v=slink[v];}
    minlen[y]=maxlen[slink[y]]+1;
    now += update(y);now += update(z);
    return z;
}
void init_sam() {
    for(int i = 1; i <= tot; ++i)
        for(int j = 0; j < 26; ++j) trans[i][j] = 0;
    pre = tot = 1;

}
int T,n;
long long ans[20000];
char a[N * 2];
struct ss{int L,R,id;}Q[20000];
int cmp(ss s1,ss s2) {
    if(s1.L == s2.L)return s1.R < s2.R;
    return s1.L < s2.L;
}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%s%d",a+1,&n);
        for(int i = 1; i <= n; ++i)
            scanf("%d%d",&Q[i].L,&Q[i].R),Q[i].id = i;
        sort(Q+1,Q+n+1,cmp);
        int l = 1,r = 0;
        for(int i = 1; i <= n; ++i) {
            if(Q[i].L != Q[i-1].L) {init_sam();
                l = Q[i].L,r = l-1;
                now = 0;
            }
            while(r < Q[i].R){
                pre = add_char(a[(++r)],pre);
            }
            ans[Q[i].id] = now;
        }
        for(int i = 1; i <= n; ++i) printf("%lld\n",ans[i]);
    }
    return 0;
}
时间: 2024-10-12 23:51:04

HDU 4622 Reincarnation 后缀自动机的相关文章

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 Reincarnation Hash解法详解

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

HDU 4622 Reincarnation(后缀自动机)

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

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 4641 K-string(后缀自动机)

题目链接:hdu 4641 K-string 题意: 一开始给你一个字符串S,现在有m个操作. 1 x表示在当前字符串末端添加一个字符x. 2 表示查询当前出现次数超过k次的子串有多少个. 题解: 后缀自动机在线维护right集. 没插入一个字符,就沿着fail跳,如果当前节点大于等于k的就不用再跳了,显然之前的节点肯定已经大于等于k了. 然后一旦有新的节点等于k就记录一下当前新增加的子串个数. 1 #include<cstdio> 2 #include<cstring> 3 #d

hdu 4436 str2int(后缀自动机)

题目链接:hdu 4436 str2int 题意: 给你n个字符串,每个字符串都是由数字构成,现在让你将这n个字符串所有的不重复子串构成的十进制数字加起来mod2012. 题解: 似乎这种不重复的子串问题,用后缀自动机都比较无脑搞. 首先将所有的串连起来,中间插个特殊字符,然后建立后缀自动机. 然后拓扑排序,从跟开始往下dp. sum[v]=Σ(sum[x]*10+cnt[x]*j),其中cnt[x]表示有多少条路径能到x这个节点,转移为cnt[v]=Σcnt[x] (x能转移到v),j为这个节

Good Article Good sentence HDU - 4416 (后缀自动机)

Good Article Good sentence \[ Time Limit: 3000 ms\quad Memory Limit: 32768 kB \] 题意 给出一个 \(S\) 串,在给出 \(n\) 个 \(T\) 串,求出 \(S\) 串中有多少子串没有在任意一个 \(T\) 串中出现过 思路 \(\quad\) 首先可以对 \(S\) 串构建后缀自动机,然后在插入 \(n\) 个 \(T\) 串,每两个串之间用 \(27\) 隔开,然后可以求出这个自动机上每个节点出现的最左位置

字符串(后缀自动机):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

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

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