HDU4622:Reincarnation(后缀自动机)

本文介绍了一种使用后缀自动机解决给定字符串区间内不同子串个数的问题。通过构建后缀自动机,利用其特性快速计算指定区间内所有不同子串的数量。文章提供了详细的实现思路及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值