【HDU6345】子串查询【前缀和】【线段树】

题目大意:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6345
求给定串llrr的位置里字典序最小的字串出现的次数。


思路:

字典序最小的串,肯定是一个字符啊。
可以用线段树维护一下。时间复杂度O(Tq logn)O(Tq\ logn)
当然也可以用前缀和。时间复杂度O(t(q+n))O(t(q+n))


代码:

线段树:

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1000100
using namespace std;

int n,t,m;
char c[N];

struct node
{
	int l,r,sum;
	char c;
}tree[N*3];

void make(int x)  //建树
{
	if (tree[x].l==tree[x].r) 
	{
		tree[x].c=c[tree[x].l];
		tree[x].sum=1;
		return;
	}
	int mid=(tree[x].l+tree[x].r)/2;
	tree[x*2].l=tree[x].l;
	tree[x*2].r=mid;
	tree[x*2+1].l=mid+1;
	tree[x*2+1].r=tree[x].r;
	make(x*2);
	make(x*2+1);
	if (tree[x*2].c==tree[x*2+1].c)  //最小字符和出现次数更改
	{
		tree[x].c=tree[x*2].c;
		tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
	}
	else if (tree[x*2].c<tree[x*2+1].c)
	{
		tree[x].c=tree[x*2].c;
		tree[x].sum=tree[x*2].sum;
	}
	else
	{
		tree[x].c=tree[x*2+1].c;
		tree[x].sum=tree[x*2+1].sum;
	}
	return;
}

int find(int x,int l,int r)
{
	if (tree[x].l==l&&tree[x].r==r)
	 return tree[x].sum*100+(int)(tree[x].c-'A');
	//这里用到了一些小技巧,本来要返回一个字符和出现次数的,但是我直接返回出现次数*100再加上字符的ASCII码值-A的ASCII码值
	if (tree[x].l==tree[x].r) 
	 return 0;
	int mid=(tree[x].l+tree[x].r)/2;
	if (r<=mid) return find(x*2,l,r);
	if (l>mid) return find(x*2+1,l,r);
	int a=find(x*2,l,mid);
	int b=find(x*2+1,mid+1,r);
	if (a%100<b%100) return a;
	if (a%100>b%100) return b;  //取出字符和ASCII码
	return (a/100+b/100)*100+a%100;  //更新
}

int main()
{
	scanf("%d",&t);
	int x,y;
	for (int q=1;q<=t;q++)
	{
		scanf("%d%d",&n,&m);
		cin>>c+1;
		tree[1].l=1;
		tree[1].r=n;
		make(1);
		printf("Case #%d:\n",q);
		while (m--)
		{
			scanf("%d%d",&x,&y);
			int a=find(1,x,y);
		    printf("%d\n",a/100);
		}
	}
	return 0;
}

前缀和:

#include <cstdio>
#include <iostream>
#include <cstring>
#define N 1000100
using namespace std;

int s[N][30],t,n,m,l,r;
char c[N];

int main()
{
	scanf("%d",&t);
	for (int q=1;q<=t;q++)
	{
		memset(s,0,sizeof(s));
		scanf("%d%d",&n,&m);
		cin>>c+1;
		for (int i=1;i<=n;i++)
		 s[i][c[i]-'A'+1]++;
		for (int i=1;i<=n;i++)
		 for (int j=1;j<=26;j++)
		  s[i][j]+=s[i-1][j];
		printf("Case #%d:\n",q);
		while (m--)
		{
			scanf("%d%d",&l,&r);
			for (int i=1;i<=26;i++)
			 if (s[r][i]-s[l-1][i])
			 {
			 	printf("%d\n",s[r][i]-s[l-1][i]);
			 	break;
			 }
		}
	}
	return 0;
}
posted @ 2018-10-07 16:37  全OI最菜  阅读(98)  评论(0编辑  收藏  举报