1. 程式人生 > >【HDU6345】子串查詢【字首和】【線段樹】

【HDU6345】子串查詢【字首和】【線段樹】

題目大意:

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6345
求給定串 l l r r 的位置裡字典序最小的字串出現的次數。


思路:

字典序最小的串,肯定是一個字元啊。
可以用線段樹維護一下。時間複雜度 O

( T q   l o g n ) 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;
}