Reincarnation
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 4497 Accepted Submission(s): 1836
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.
Author
WJMZBMR
Source
2013 Multi-University Training Contest 3
题意:给一个字符串,Q个询问,每个询问输出对应区间内的不同的子串个数。
思路:
学习后缀自动机,后缀自动机就是一个有向图,有若干个终点,从起点出发沿任意路径到任意终点都是原串的一个后缀,核心部分是pre数组,即“位置集合”的对应关系,跟KMP的pre数组有异曲同工之处,具体可以看网上的资料学习。代码的核心部分在于计算pre数组,当建边发生冲突时,代码里面要判断mx[q] == mx[p] + 1,我的理解是如果条件为真,说明从p可以一步直达q,因此q可以作为一个终点;如果条件为假,q就不能作为终点,因为说明p经过其他点到达q,会产生一个不存在的后缀,这时候要建立一个从p点直达的nq。mx数组是记录一个“位置集合”的最长子串,显然跟pre集合的mx值相减就是新增的子串。 建图复杂度是O(n),本题O(n^2)建图暴力计算所有情况就行。
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int maxn = 2e3+30;
char str[maxn];
int g[maxn<<1][26], pre[maxn<<1], mx[maxn<<1], ans[maxn][maxn];
int sz, last, tot;
void newnode(int s)
{
mx[++sz] = s;
pre[sz] = 0;
memset(g[sz], 0, sizeof(g[sz]));
}
void init()
{
sz = tot = 0;
last = 1;
newnode(0);
}
int ins(int x)
{
newnode(mx[last] + 1);
int p = last, np = sz;
while(p && !g[p][x])
{
g[p][x] = np;
p = pre[p];
}
if(p)//边发生冲突
{
int q = g[p][x];
if(mx[q] == mx[p] + 1) pre[np] = q;//新增结束点,不改变原图
else
{
newnode(mx[p] + 1);//新增点
int nq = sz;
for(int j=0; j<26; ++j) g[nq][j] = g[q][j];
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
while(p && g[p][x] == q)
{
g[p][x] = nq;
p = pre[p];
}
}
}
else
pre[np] = 1;
tot += mx[np] - mx[pre[np]];
last = np;
return tot;
}
int main()
{
int T, q, l, r;
for(scanf("%d",&T); T; --T)
{
scanf("%s%d",str,&q);
int len = strlen(str);
init();
for(int i=0; i<len; ++i,init())
for(int j=i; j<len; ++j)
ans[i+1][j+1] = ins(str[j]-'a');
while(q--)
{
scanf("%d%d",&l,&r);
printf("%d\n",ans[l][r]);
}
}
return 0;
}