版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82959023
大意
给定一个长度为 的串,回答各区间内字典序最小的子串的总数
思路
字典序最小的字串,其实就是
码最小的那个字母构成的子串,也就是说,我们需要查询区间最小值并且返回其个数,这种构造使得本题有多种解决方案(只介绍
的)
注:以下算法均要结合前缀和优化才能过
- 线段树/平衡树,优点:扩展性好,支持带修改,缺点:常数大,速度慢,会被卡
- ST表 优点:效率高,缺点:容易被卡,数据大点过不了
- 利用字母只有26个的特点,枚举所有字母,利用前缀和获取区间个数,判断是否为正数即可,优点:简单,易懂,效率较高,缺点:局限性大,当出现的字符种类特别多时会被卡
- 序列自动机,速度最快
大佬的方法
#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 )
#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);//输出
}
}
}