Contest Hunter - IHHH

题意:

给出n个字符串和m个询问;

每个询问有l,r和一个字符串;

查询[l,r]区间中的所有是询问字符串的子串的最大长度;

n<=100000,m<=100000,字符串总长度<=500000;

此题为CH【弱省胡策】 #1 T2;


题解:

一道好题,感觉正解说起来只是一句话但是真是很有道理。。

就是说:将询问拆成log个区间,对线段树每个结点建AC自动机分别处理;

这样每个询问都只询问了log个区间,每个字符串也只被log个结点覆盖;

时间复杂度O(nlogn*26),代码极其好写。。

实际上这样也就是处理区间问题的基本方法之一,拆分成log个区间来做,用线段树一样的分析来保证复杂度;

不要忘了取模哦XD


代码:


#include<queue>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define LEN 610000
#define S 26
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
#define vec vector<int>
#define iter vec::iterator
using namespace std;
char buf1[LEN];
char* str[N];
char buf2[LEN];
char* p[N];
int L[N],R[N];
int ans[N];
vec query[N<<2];
int next[LEN][S],fail[LEN],len[LEN],tot;
queue<int>q;
int newnode()
{
	int ret=++tot;
	memset(next[ret],0,sizeof(int)*S);
	fail[ret]=len[ret]=0;
	return ret;
}
void init()
{
	tot=0;
	newnode();
}
void Insert(char* s)
{
	int p=1,index,l=0;
	while(*s!='\0')
	{
		index=*s-'a';
		if(!next[p][index])
			next[p][index]=newnode();
		p=next[p][index];
		s++;
		l++;
	}
	len[p]=max(len[p],l);
}
void Build()
{
	int x,i,temp,p;
	q.push(1);
	while(!q.empty())
	{
		x=q.front(),q.pop();
		for(i=0;i<S;i++)
		{
			if(next[x][i])
			{
				p=next[x][i];
				temp=fail[x];
				while(temp&&next[temp][i]==0)
					temp=fail[temp];
				if(temp)
					fail[p]=next[temp][i],len[p]=max(len[fail[p]],len[p]);
				else
					fail[p]=1;
				q.push(p);
			}
			else
			{
				temp=fail[x];
				while(temp&&next[temp][i]==0)
					temp=fail[temp];
				if(temp)
					next[x][i]=next[temp][i];
				else
					next[x][i]=1;
			}
		}
	}
}
int calc(char* s)
{
	int p=1,ret=0;
	while(*s!='\0')
	{
		p=next[p][*s-'a'];
		ret=max(ret,len[p]);
		s++;
	}
	return ret;
}
void update(int l,int r,int no,int st,int en,int val)
{
	if(st<=l&&r<=en)
		query[no].push_back(val);
	else
	{
		int mid=l+r>>1;
		if(en<=mid)		update(lson,st,en,val);
		else if(st>mid)	update(rson,st,en,val);
		else	update(lson,st,en,val),update(rson,st,en,val);
	}
}
void slove(int l,int r,int no)
{
	if(!query[no].empty())
	{
		init();
		for(int i=l;i<=r;i++)
			Insert(str[i]);
		Build();
		for(iter it=query[no].begin();it!=query[no].end();it++)
			ans[*it]=max(calc(p[*it]),ans[*it]);
	}
	if(l==r)	return ;
	int mid=l+r>>1;
	slove(lson);
	slove(rson);
}
int main()
{
	int n,m,i,j,k,x,y;
	scanf("%d%d",&n,&m);
	str[0]=buf1,p[0]=buf2;
	for(i=1;i<=n;i++)
	{
		str[i]=str[i-1]+strlen(str[i-1])+1;
		scanf("%s",str[i]);
	}
	for(i=1;i<=m;i++)
	{
		p[i]=p[i-1]+strlen(p[i-1])+1;
		scanf("%d%d%s",L+i,R+i,p[i]);
		update(1,n,1,L[i],R[i],i);
	}
	slove(1,n,1);
	for(i=1;i<=m;i++)
		printf("%d\n",ans[i]%998244353);//千万别忘了取模!!!! 
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值