2018年10月7日提高组模拟赛 T2 字串数量

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82959023

大意

给定一个长度为 n n 的串,回答各区间内字典序最小的子串的总数


思路

字典序最小的字串,其实就是 A S C L L ASCLL 码最小的那个字母构成的子串,也就是说,我们需要查询区间最小值并且返回其个数,这种构造使得本题有多种解决方案(只介绍 A C AC 的)
注:以下算法均要结合前缀和优化才能过

  1. 线段树/平衡树,优点:扩展性好,支持带修改,缺点:常数大,速度慢,会被卡
  2. ST表 优点:效率高,缺点:容易被卡,数据大点过不了
  3. 利用字母只有26个的特点,枚举所有字母,利用前缀和获取区间个数,判断是否为正数即可,优点:简单,易懂,效率较高,缺点:局限性大,当出现的字符种类特别多时会被卡
  4. 序列自动机,速度最快

W Y C WYC 大佬的方法

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int t,n,m,l,r,yg,cs[26][1000001];
char s[1000001];
inline int read()
{
	char c;int d=1,f=0;
	while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),c>47&&c<58)f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void write(register long long x){if(x>9)write(x/10);putchar(x%10+48);return;}
signed main()
{
	t=read();
	while(t--)
	{
		n=read();m=read();
		memset(cs,0,sizeof(cs));
		scanf("%s",s+1);
		for(register int i=1;i<=n;cs[s[i]-65][i]++,i++)
		for(register int j=0;j<26;j++)cs[j][i]=cs[j][i-1];
		while(m--)
		{
			l=read();r=read();
			for(register int wz=0;wz<26;wz++)
			if(cs[wz][r]-cs[wz][l-1]>0)
			{write(cs[wz][r]-cs[wz][l-1]);putchar(10);break;}
		}
	}
}

ST表代码(5140 m s ms )

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int t,n,m,l,r,cx[26][1000001],yg,cs[26][1000001],wz,log[1000001],f[1000001][25];
char s[1000001];
inline int read()
{
	char c;int d=1,f=0;
	while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),c>47&&c<58)f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void write(register long long x){if(x>9)write(x/10);putchar(x%10+48);return;}//输入输出优化
inline int query(register int x,register int y)//回答
{
	int z=log[y-x+1];
	return min(f[x][z],f[y-(1<<z)+1][z]);
}
signed main()
{
	t=read();log[2]=1;
	for(register int i=3;i<1000001;i++) log[i]=log[i>>1]+1;
	while(t--)
	{
		n=read();m=read();
		memset(f,127/3,sizeof(f));memset(cs,0,sizeof(cs));
		scanf("%s",s+1);
		for(register int i=1;i<=n;i++)f[i][0]=s[i]-65;
		for(register int i=1;i<=n;cs[s[i]-65][i]++,i++)
		for(register int j=0;j<26;j++)cs[j][i]=cs[j][i-1];//前缀和计算每个字母的出现次数
		for(register int j=1;(1<<j)<=n;j++)
		 for(register int i=1;i+(1<<j)-1<=n;i++)
		  f[i][j]=min(f[i][~-j],f[i+(1<<(j-1))][~-j]);//预处理
		while(m--)
		{
			l=read();r=read();
			wz=query(l,r);
			write(cs[wz][r]-cs[wz][l-1]);putchar(10);//输出
		}
	}
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82959023