[HDU4622]Reincarnation-后缀自动机

Reincarnation

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.

是谁说这题是后缀自动机入门题的……想了好久……

//掀桌.jpg
┏┻┻┻┻┻┻┻┓           |             |
┃  ┏┓  ┏┓ ┃           |             |
┃  ┗┛ A┗┏━┓ ┏━┓━━┻━━━━━━━┻━━━
┗━━━━━┗━┛ ┗━┛
     |   |

这真的是一道入门题???而不是一道进阶题???
我还是太弱了╮( ̄▽ ̄”)╭~~

思路:
由于咱水平有限,后缀自动机在这题只是负责数数的……
思路是预处理,对于原串每一个后缀建一次自动机。
由于自动机的性质,加一个点增加的方案数就是以该点为后缀的子串个数。
所以爆枚起点(0-n)并对每一个起点建自动机。
这时每插入一个新节点就能处理出从当前起点到当前新节点的方案数了。
预处理出表后直接查表即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=5233;

inline int read()
{
    int x=0;
    char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x;
}

int ans[2333][2333];

struct SAM
{
    int next[N][26],fa[N],len[N],cnt[N];
    int pool,u;

    void init()
    {
        pool=u=1;
        memset(next,0,sizeof(next));
        memset(len,0,sizeof(len));
        memset(cnt,0,sizeof(cnt));
        memset(fa,0,sizeof(fa));
        cnt[pool]=1;
    }

    int insert(int v)
    {
        int now=++pool;
        len[now]=len[u]+1;

        while(!next[u][v] && u)
            cnt[now]+=cnt[u],next[u][v]=now,u=fa[u];

        if(!u)
            fa[now]=1;
        else
        {
            int q=next[u][v];
            if(len[q]==len[u]+1)
                fa[now]=q;
            else
            {
                int newq=++pool;

                memcpy(next[newq],next[q],sizeof(next[q]));
                len[newq]=len[u]+1;
                fa[newq]=fa[q];
                cnt[newq]=0;

                fa[now]=fa[q]=newq;

                while(next[u][v]==q && u)
                {
                    cnt[next[u][v]]-=cnt[u];  
                    cnt[newq]+=cnt[u];
                    next[u][v]=newq,u=fa[u];
                }
            }
        }

        u=now;
        return now;
    }
}koishi;

int main()
{
    int T=read();
    char s[N];

    while(T--)
    {
        scanf("%s",&s);

        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            koishi.init();
            for(int j=i;j<len;j++)
            {
                koishi.insert(s[j]-'a');
                ans[i][j]=koishi.cnt[koishi.u];
            }

            for(int j=i+1;j<len;j++)  
                ans[i][j]+=ans[i][j-1]; 
        }

        int q=read();
        while(q--)
        {
            int a=read(),b=read();
            printf("%d\n",ans[a-1][b-1]);
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值