2018”百度之星”程序设计大赛 - 资格赛 子串查询
题意:给你n长度一个字符串找字符串中,区间 [ l , r ]间最小的字串的个数。最小字串的定义为AA小于AAA AB小于BA。有q次查询。
思路:这道题换个意思就是说,找区间最小的字母的个数。 将字母变成数,找区间最小值的个数。要用到线段树,只要建树和查询就好。这里也是用到线段树的找区间最小值的模板,然后加以修改就过了
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
const int inf=0x7f7f7f7f;
int num[maxn];
struct node{
int left,right,val;//左右最小值
int cnt;
}c[maxn*4];
void build(int l,int r,int root)
{
c[root].left=l;
c[root].right=r;
if(l==r)//z作曲兼等于右区间
{
c[root].val=num[l];
c[root].cnt=1;
return;
}
int mid=(l+r)/2;
build(l,mid,root*2);
build(mid+1,r,root*2+1);
//c[root].val=min(c[root*2].val,c[root*2+1].val);
if(c[root*2].val<c[root*2+1].val)
c[root].val=c[root*2].val,c[root].cnt=c[root*2].cnt;
else if(c[root*2].val>c[root*2+1].val)
c[root].val=c[root*2+1].val,c[root].cnt=c[root*2+1].cnt;
else
c[root].val=c[root*2+1].val,c[root].cnt=c[root*2+1].cnt+c[root*2].cnt;//cout<<"c[coot] "<<c[root].cnt<<endl;
}
void query(int l,int r,int &min1,int root,int &cnt)
{
if(c[root].left==l&&c[root].right==r)
{
min1=c[root].val;
cnt=c[root].cnt;
return;
}
int mid=(c[root].left+c[root].right)/2;
if(mid<l)//找到所要找到的区间范围
query(l,r,min1,root*2+1,cnt);
else if(mid>=r)
query(l,r,min1,root*2,cnt);
else//找到索要找的区间
{
int min2,cnt2=0;
query(l,mid,min1,root*2,cnt);//左区间最小值以及他的个数
query(mid+1,r,min2,root*2+1,cnt2);//右区间最小值以及它的个数
if(min2<min1)
{
cnt=cnt2;
min1=min2;
}
else if(min2==min1)
{
cnt+=cnt2;
}
}
}
int main()
{
int t,n,q,kcase=1;
string s;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
cin>>s;
//求区间内单个字符最小的,并且这个最小的个数
for(int i=1;i<=n;i++)
num[i]=s[i-1]-'A';
build(1,n,1);
printf("Case #%d:\n",kcase++);
while(q--)
{
int l,r,min1=inf,cnt=0;
scanf("%d%d",&l,&r);
query(l,r,min1,1,cnt);
//printf("min1= %d cnt1= %d\n",min1,cnt);
printf("%d\n",cnt);
}
}
return 0;
}
/*
4
5 5
AABCA
1 3
*/