HDU 4622 Reincarnation

ProblemDescription

Now you are back,andhave a task to do:
Given you a string s consist of lower-case English letters only,denote f(s) asthe 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 linecontains 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 numberof queries.
Then Q lines follows,each lines contains two integer l, r(1 <= l <= r<= n), denote a query.

 

 

Output

For each testcases,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

题目大意:给一个字符串,进行q次询问,求lr之间的字符串有多少个不同子串。

方法:开始用后缀数组暴力做的,结果果断T掉了。后来看了看CLJ的后缀自动机,然后在网上有查了些后缀自动机的资料(感觉这篇写得不错。http://blog.csdn.net/xuezhongfenfei/article/details/12262941)就试着用后缀自动机写了。对于后缀自动机。我们先将lr按照l从小到大,排序。对于l相同,r从小到大排序。这样对于l相同的询问,就可以利用后缀自动机的特性往后面加字符得到答案。对于l不同的询问,就重新建立自动机。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<deque>
#include<algorithm>
using namespace std;

struct NODE
{
	int left ,right;
	int num;
};

struct State
{
	State *pre ,*go[26];//pre是上一个可以接收后缀的结点
	int val;
};

State * root ,* tail ,que[4010];
NODE nodes[10010];
int ans[10010];
int n ,q ,tot ,sum ,len;
char str[2010];

int cmp(NODE a ,NODE b)
{
	if(a.left==b.left)
	{
		return a.right < b.right;
	}
	return a.left < b.left;
}

void init()
{
	memset(que,0,sizeof(que));
	tot = 0;
	len = 0;
	root = tail = &que[tot++];
}

void add(int w )
{
	State *p = tail ,*np = &que[tot++];
	np->val = len;
	//找到上一个能接受后缀的节点
	while(p && p->go[w]==NULL)
	{
		p->go[w] = np;
		p = p->pre;
	}
	//如果没有找到这样的节点,就将pre指针指向root
	if(p==NULL)
	{
		np->pre = root;
	}
	//如果找到这样的节点,就把这个节点pre就指向这个节点对应的儿子
	else
	{
		State *q = p->go[w];
		if(p->val + 1==q->val)
		{
			np->pre = q;
		}
		else
		{
			State *nq = &que[tot++];
			//将q的指针赋值给nq,将nq的pre指向p,将np和q的pre指向nq
			*nq = *q;
			nq->val = p->val + 1;
			q->pre = np->pre = nq;
			//同时将p的儿子为w且指向q的全部指向nq
			while(p && p->go[w]==q)
			{
				p->go[w] = nq;
				p = p->pre;
			}
		}
	}
	tail = np;
}

int solve()
{
    sum = 0;
    for(int i = tot - 1;i > 0;i--)
    {
       sum += que[i].val - que[i].pre->val;
    }
	return sum;
}

int main()
{
	int x;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%s",str+1);
		scanf("%d",&q);
		for(int i = 1;i<=q;i++)
        {
			scanf("%d%d",&nodes[i].left,&nodes[i].right);
			nodes[i].num = i;
        }
		sort(nodes + 1,nodes + q + 1,cmp);
		nodes[0].left = -1;
		for(int i = 1;i<=q;i++)
		{
			if(nodes[i].left!=nodes[i-1].left)
			{
				init();
				for(int j = nodes[i].left;j <= nodes[i].right;j++)
				{
					len++;
					add(str[j] - 'a');

				}

				ans[nodes[i].num] = solve();
				x = nodes[i].right + 1;
			}
			else
			{
				if(nodes[i].right==nodes[i-1].right)
				{
					ans[nodes[i].num] = ans[nodes[i-1].num];
				}
				else
				{
					for(int j = x;j <= nodes[i].right;j++)
					{
						len++;
						add(str[j] - 'a');
					}
					ans[nodes[i].num] = solve();
					x = nodes[i].right + 1;
				}
			}
		}
		for(int i = 1;i <= q;i++)
		{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值