bzoj 5384: 有趣的字符串题

题意

白兔有一个长度为?的字符串。
白云有?个询问,每个询问会询问一段区间的本质不同回文子串个数。

题解

挺好玩的题
先说一个暴力的做法,就是建立PAM
然后离线, r r r端点一个一个加,维护l的答案
然后每一次,就暴力找到以 r r r结尾的所有回文串,再找到上一次出现的位置,这个可以在PAM的fail树上用线段树维护,然后用树状数组资瓷区间加即可
那么复杂度就是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
考虑优化,每一次加入所有字符串太慢了

有一个结论是
r r r结尾的回文串,可以被分成 l o g log log段等差数列
具体思想来自:金策_字符串算法选讲
但是我个人感觉这题最主要的结论不是这个
我们可以用同样的思想
对于每一个回文串分组,也是按照 2 i 2^i 2i来分组
每一组里面,你会发现,他们的区间加可以合在一起,并成一段连续的区间
那么我们只需要暴力遍历每log组,每次修改就可以了
至于为什么可以合在一起,因为他们上一次出现的位置恰好就是同一组里面的上一个
因为他们的长度都不相差一倍
于是对于每一组,暴力做就OK了

于是复杂度就是的 n l o g 2 n nlog^2n nlog2n
em…学习了 2 i 2^i 2i分组的思想以及PAM的姿势

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int,int> PI;
typedef long long LL;
const int N=300005;
const int M=1000005;
const int MOD=1e9+7;
int add (int x,int y)   {x=x+y;return x>=MOD?x-MOD:x;}
int mul (int x,int y)   {return (LL)x*y%MOD;}
int dec (int x,int y)   {x=x-y;return x<0?x+MOD:x;}
int Pow (int x,int y)
{
	if (y==1) return x;
	int lalal=Pow(x,y>>1);
	lalal=mul(lalal,lalal);
	if (y&1) lalal=mul(lalal,x);
	return lalal;
}
int n,m;
char ss[N];
struct qq
{
	int son[N][26];
	int fail[N];
	int len[N];
	int s[N],num,n,last;
	int d[N],f[N];
	void Init ()	{num=1;fail[0]=1;len[1]=-1;len[0]=0;s[0]=-1;}
	int find (int x,int xx)
	{
		while (s[xx-len[x]-1]!=s[xx]) x=fail[x];
		return x;
	}
	void Ins (int x)
	{
		s[++n]=x;
		int now=find(last,n);
		//printf("YES:%d %d %d %d\n",now,x,s[n],n-len[now]-1);
		if (son[now][x]==0)
		{
			num++;	
			fail[num]=son[find(fail[now],n)][x];
			son[now][x]=num;
			len[num]=len[now]+2;
			//printf("NO:%d %d\n",now,num);
			d[num]=len[num]-len[fail[num]];
			if (d[num]==d[fail[num]])  f[num]=f[fail[num]];
			else f[num]=num;
		}
		//printf("YES:%d\n",fail[num]);
		last=son[now][x];
	}
	vector<int> vec[N];
	void link ()
	{
		for (int u=0;u<=num;u++)
			if (u!=1)
				vec[fail[u]].push_back(u);
	}
	int L[N],R[N],id;
	void dfs (int x)
	{
		L[x]=++id;
		int siz=vec[x].size();
		for (int u=0;u<siz;u++) dfs(vec[x][u]);
		R[x]=id;
	}
}PAM;
struct qt
{
	int l,r;
	int s1,s2;
	int mx;
}tr[N*2];int num;
void update (int now)
{
	int s1=tr[now].s1,s2=tr[now].s2;
	tr[now].mx=max(tr[s1].mx,tr[s2].mx);
}
void bt (int l,int r)
{
	int now=++num;
	tr[now].l=l;tr[now].r=r;
	if (l==r) return ;
	int mid=(l+r)>>1;
	tr[now].s1=num+1;bt(l,mid);
	tr[now].s2=num+1;bt(mid+1,r);
	update(now);
}
int query (int now,int l,int r)
{
	if (tr[now].l==l&&tr[now].r==r)	return tr[now].mx;
	int mid=(tr[now].l+tr[now].r)>>1;
	int s1=tr[now].s1,s2=tr[now].s2;
	if (r<=mid) return query(s1,l,r);
	else if (l>mid) return query(s2,l,r);
	else return max(query(s1,l,mid),query(s2,mid+1,r));
}
void change (int now,int x,int id)
{
	if (tr[now].l==tr[now].r)	{tr[now].mx=id;return ;}
	int s1=tr[now].s1,s2=tr[now].s2;
	int mid=(tr[now].l+tr[now].r)>>1;
	if (x<=mid) change(s1,x,id);
	else change(s2,x,id);
	update(now);
}
int f[N];
int lb (int x)	{return x&(-x);}
void modify (int x,int c)	{while (x<=n){f[x]+=c;x+=lb(x);}}
int get (int x)	{int lalal=0;while (x>0){lalal=lalal+f[x];x-=lb(x);}return lalal;}
vector<PI> Q[N];
int main()
{
	PAM.Init();
	scanf("%d%d",&n,&m);
	scanf("%s",ss+1);
	for (int u=1;u<=n;u++) PAM.Ins(ss[u]-'a');
	/*for (int u=1;u<=PAM.num;u++)
		printf("%d %d\n",PAM.len[u],PAM.fail[u]);*/
	PAM.link();
	PAM.dfs(1);
	bt(1,PAM.id);
	for (int u=1;u<=m;u++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		Q[r].push_back({u,l});
	}
	int now=1;
	int ans=0;
	for (int u=1;u<=n;u++)
	{
		now=PAM.son[PAM.find(now,u)][ss[u]-'a'];
		//printf("%d\n",now);
		for (int i=now;i!=0;i=PAM.fail[PAM.f[i]])
		{
			int t=query(1,PAM.L[i],PAM.R[i]);
			modify(max(1,t-PAM.len[i]+2),1);
			modify(u-PAM.len[PAM.f[i]]+2,-1);
		}
		change(1,PAM.L[now],u);
		int siz=Q[u].size();
		for (int i=0;i<siz;i++) 
		{
			//printf("%d %d %d\n",Q[u][i].first,Q[u][i].second,get(Q[u][i].second));
			ans=add(ans,mul(get(Q[u][i].second),Q[u][i].first));
		}
	}
	printf("%d\n",ans);
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值