HDU - 4622 Reincarnation 后缀自动机

Reincarnation

  HDU - 4622


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 l...r), s l...r 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.

Source HDU - 4622
My Solution
题意:给出一个长度<=2000的字符串,然后给出q<=10000个询问,为s[l,r]内有多少个不同的子串。 后缀自动机 首先n <= 2000,故可以用O(n^2)的算法,故对于每个 i ~ n-1建立n个后缀自动机,每次添加一个字符就多出val[np] - val[pre[np]]个不同的子串, valx]表示x节点存储的字符串个数即能表示的最长子串。 故用sum[maxn][maxn] n个前缀和来存储区间内不同子串的个数。 复杂度 O(n^2)
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 2*2e3 + 8;

string s;
struct SAM{
    int ch[maxn][26], pre[maxn], val[maxn];
    int last, tot;
    void init(){
        last = tot = 0;
        memset(ch[0], -1, sizeof ch[0]);
        pre[0] = -1; val[0] = 0;
    }
    int extend(int c){
        int p = last, np = ++tot;
        val[np] = val[p] + 1;
        memset(ch[np], -1, sizeof ch[np]);
        while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
        if(p == -1) pre[np] = 0;
        else{
            int q = ch[p][c];
            if(val[q] != val[p] + 1){
                int nq = ++tot;
                memcpy(ch[nq], ch[q], sizeof ch[q]);
                val[nq] = val[p] + 1;
                pre[nq] = pre[q];
                pre[q] = pre[np] = nq;
                while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
            }
            else pre[np] = q;
        }
        last = np;
        return val[np] - val[pre[np]];
    }
} sam;

int sum[maxn][maxn];

int main()
{
    #ifdef LOCAL
    freopen("13.in", "r", stdin);
    //freopen("13.out", "w", stdout);

    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);
    int T, n, i, j, q;
    cin >> T;
    while(T--){
        cin >> s;
        n = s.size();
        for(i = 0; i < n; i++){
            sam.init(); sum[i+1][0] = 0;
            for(j = i; j < n; j++) sum[i+1][j+1] = sum[i+1][j] + sam.extend(s[j] - 'a');
        }
        cin >> q;
        while(q--){
            cin >> i >> j;
            cout << sum[i][j] << "\n";
        }
    }
    return 0;
}


Thank you!                                                                                                                                              ------from  ProLights

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值