首页 > 其他分享 >[NOI2023] 字符串

[NOI2023] 字符串

时间:2023-07-31 20:23:57浏览次数:35  
标签:val int void 后缀 NOI2023 数组 字符串 回文

对于给出的串 \(S\),将其拓展成 \(S+\) 特殊字符 \(+rev(S)\) ,求出其后缀数组。

那么对于一个子串 \([l,r]\),合法的必要条件是 \(l\) 的后缀在后缀数组的排名小于 \(r\) 的前缀的排名。

之所以是必要条件,是因为会记入一些 \([l,r]\) 是回文串且 \(l\) 的排名小的情况。

具体而言,这种情况会发生,当且仅当:我们求出向左右两边拓展直到两边字符所能拓展的最长回文串,那么要求这个回文串左右两边不为空,且右边的字典序小于左边的。

我们考虑用第一种情况减掉第二种情况。

第一种情况求出后缀数组之后是个简单的二维数点,第二种考虑枚举一个回文中心,那么只有以其为中心的最长回文串是有用的,如果其满足第二个情况,也可以列出一个二维数点的式子,都可以离线树状数组解决。

复杂度 \(O((n+m)\log n)\) 。

因为不会后缀数组,所以采用的是求出后缀树然后再建出后缀数组的方式。

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+7;
int n,q;
char s[N];
int tr[N][27],son[N][27],len[N],fa[N],pos[N],key[N];
int tot=1,last=1;
void init()
{
	for(int i=1;i<=tot;i++)
	{
		len[i]=fa[i]=pos[i]=key[i]=0;
		for(int c=0;c<27;c++)
		tr[i][c]=son[i][c]=0;
	}
	tot=last=1;
} 
void copy(int a,int b)
{
	len[a]=len[b];
	fa[a]=fa[b];
	pos[a]=pos[b];
	for(int c=0;c<27;c++)
	tr[a][c]=tr[b][c];
}
void Extend(int c,int x)
{
	int p=last,np=last=++tot;
	len[np]=len[p]+1;
	pos[np]=x;
	key[np]=1;
	while(p&&!tr[p][c])
	{
		tr[p][c]=np;
		p=fa[p];
	}
	if(!p)fa[np]=1;
	else 
	{
		int q=tr[p][c];
		if(len[q]==len[p]+1)fa[np]=q;
		else 
		{
			int nq=++tot;
			copy(nq,q);
			len[nq]=len[p]+1;
			fa[np]=fa[q]=nq;
			while(p&&tr[p][c]==q)
			{
				tr[p][c]=nq;
				p=fa[p];
			}
		}
	}
}
int sa[N],rk[N],top=0;
void getsa(int x)
{
	if(key[x]) 
	{
		sa[++top]=pos[x];
		rk[pos[x]]=top;
	}
	for(int c=0;c<27;c++)
	if(son[x][c])getsa(son[x][c]);
}
char str[N];
int h[N],range[N]; 
void manacher()
{
	int p=0,r=0,c=0;
	for(int i=1;i<=top;i++)h[i]=0;
	for(int i=1;i<=top;i++)
	{
		if(i<=r)h[i]=min(h[2*p-i],r-i+1);
		while(str[i-h[i]]==str[i+h[i]])h[i]++;
		if(str[i]>='a'&&str[i]<='z') c++;
		if(str[i]=='#'&&i>2) range[c]=h[i]/2;
		if(i+h[i]-1>r) 
		{
			p=i;
			r=i+h[i]-1;
		}
	}
}
struct BIT 
{
	int c[N],m;
	void clr(int _m)
	{
		for(int i=1;i<=m;i++)c[i]=0;
		m=_m;
	}
	void upd(int x,int v)
	{
		for(int i=x;i<=m;i+=i&-i)
		c[i]+=v;
	}
	int ask(int x)
	{
		int res=0;
		for(int i=x;i;i-=i&-i)
		res+=c[i];
		return res;
	}
	int qry(int l,int r){return ask(r)-ask(l-1);}
}C[2],D;
struct Query 
{
	int id,x,r,op;
};
vector<Query> Q[N],Q2[N];
int ans[N];
void solve()
{
	scanf("%d %d",&n,&q);
	scanf("%s",s+1);
	init();
	int val=0;
	for(int i=1;i<=n;i++)s[++val]=s[i];
	s[++val]='a'+26;
	for(int i=n;i>=1;i--)s[++val]=s[i];
	for(int i=1;i<=val;i++)Extend(s[i]-'a',i);
	for(int i=2;i<=tot;i++)son[fa[i]][s[pos[i]-len[fa[i]]]-'a']=i;
	top=0;getsa(1);
	top=0;
	str[++top]='$';
	str[++top]='#';
	for(int i=1;i<=n;i++)str[++top]=s[i],str[++top]='#';
	str[++top]='&';
	manacher();
	C[0].clr(val);C[1].clr(val);D.clr(n);
	for(int i=1;i<=n;i++)Q[i].clear(),Q2[i].clear();
	for(int i=1;i<=q;i++)
	{
		int x,r;
		ans[i]=0;
		scanf("%d %d",&x,&r);
		int u=n-x+2+n;
		//int res=0;
		Q[x-1].push_back((Query){i,u,r,-1});
		Q[x+2*r-1].push_back((Query){i,u,r,1});
		Q2[x-1].push_back((Query){i,x,r,-1});
		Q2[x+r-1].push_back((Query){i,x,r,1});
//		for(int i=1;i<=r;i++) if(rk[x+2*i-1]>rk[u])res++;
//		for(int i=x;i<=n;i++)
//		{
//			int pl=i-range[i]+1,pr=i+range[i];
//			if(pl>1&&pr<n&&pl<=x&&i<=x+r-1) res-=(s[pl-1]>s[pr+1]); 
//		}
//		printf("%d\n",res);
	}
	for(int i=1;i<=n;i++)
	{
		C[i&1].upd(rk[i],1);
		int pl=i-range[i]+1,pr=i+range[i];
		if(pl>1&&pr<n&&s[pl-1]>s[pr+1]) D.upd(pl,1);
		for(auto U:Q[i]) ans[U.id]+=1ll*U.op*C[i&1].qry(rk[U.x]+1,val);
		for(auto U:Q2[i]) ans[U.id]-=1ll*U.op*D.ask(U.x);
	}
	for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
}
int main()
{
	int tp,T;
	cin>>tp>>T;
	while(T--)
	{
		solve();
	}
	return 0;
} 

标签:val,int,void,后缀,NOI2023,数组,字符串,回文
From: https://www.cnblogs.com/jesoyizexry/p/17594384.html

相关文章

  • P9482 [NOI2023] 字符串
    P9482[NOI2023]字符串限制长的很像回文串,但是是字典序关系。定睛一看比较的是原串\(s\)的一个后缀的前缀和翻转串\(s'\)的一个后缀的前缀比字典序。直接把\(s'\)拼到\(s\)后面,中间加个分隔符,来一次后缀排序。排名小的后缀字典序比排名大的后缀小。设当前比较的是......
  • java设置字符串颜色
    如何实现Java设置字符串颜色概述本文将向刚入行的小白开发者介绍如何在Java中设置字符串颜色。我们将使用Java的控制台输出来展示不同颜色的字符串。首先,我们将介绍整个实现的流程,然后逐步讲解每个步骤所需的代码和注释。实现流程步骤描述1.导入必要的类和包2.创......
  • LC 8、字符串转换整数(atoi)
    LC8、字符串转换整数(atoi)题目描述Leetcode上的8、字符串转换整数(atoi),难度为中等请你来实现一个myAtoi(strings)函数,使其能将字符串转换成一个32位有符号整数(类似C/C++中的atoi函数)。函数myAtoi(strings)的算法如下:读入字符串并丢弃无用的前导空格检查下......
  • 字符串基础
    几乎所有字符串算法都存在一个共性:基于所求信息的特殊性质与已经求出的信息,使用增量法与势能分析求得所有信息。这体现了动态规划思想。Manacher很好地证明了这一点:它维护所求得的最右回文子串的回文中心\(d\)与回文半径\(r\),利用回文性质通过均摊右端点移动距离在线性时间内......
  • mysql 字符串包含某个字符
    MySQL字符串包含某个字符在MySQL中,我们常常需要对字符串进行各种操作,包括判断一个字符串是否包含某个字符。本文将介绍如何使用MySQL语句来判断字符串是否包含某个字符,以及提供相应的代码示例。使用LIKE语句MySQL中提供了LIKE语句来判断一个字符串是否包含某个字符。LIKE语句是......
  • python 接口返回存储json字符串包含\n
    实现“python接口返回存储json字符串包含\n”的步骤为了实现接口返回存储包含特殊字符\n的JSON字符串,我们需要按照以下步骤进行操作:步骤描述1创建一个Python接口2生成包含特殊字符\n的JSON字符串3返回JSON字符串现在,让我们一步步实现这个过程。步骤1:创建......
  • 【ACM专项练习#02】整行字符串、输入vector、打印图形、处理n组数据以及链表操作等
    输入整行字符串平均绩点题目描述每门课的成绩分为A、B、C、D、F五个等级,为了计算平均绩点,规定A、B、C、D、F分别代表4分、3分、2分、1分、0分。输入有多组测试样例。每组输入数据占一行,由一个或多个大写字母组成,字母之间由空格分隔。输出每组输出结果占一行。如果输入的大......
  • P3375 【模板】KMP 字符串匹配 题解
    前言狗屁不是,建议别看!!! 题目链接P3375【模板】KMP字符串匹配-洛谷|计算机科学教育新生态(luogu.com.cn) 分析先给个例子s1:ABCABCABBs2:ABCABB若使用朴素算法匹配,当匹配到s1:ABCABCABBs2:ABCABB时,朴素算法会跳出,然后匹配下一位。最终匹配到s1:ABCABCABBs2:......
  • 2023 联合省选-PKUSC2023-NOI2023游记
    在这段时间主要在学文化课,没怎么停课,天天暴力拼盘,所以索性合在一起。感觉非常意识流,和OI关系好像也不大。pig嫌我开始写的太短,我积极听取他人建议,加了一车流水账。联赛结束以后就退役了。因为即使NGOI也大概率会被卡“省线”,但还打算参加省选碰碰运气。遂在省选前两周申请一周半......
  • 字符串编码-Unicode
    作为程序员难免会与字符串打交道,而字符串的编码方式接触得最多的就是ASCII码了,然而ASCII码每个字母对应1Byte,因此字母总量最多只有256个,这是不能满足世界上众多的文字的需求的,因此,Unicode编码的出现便是必然的。UnicodeUnicode 为世界上所有字符都分配了一个唯一的数字编号,这个......