hiho230 Smallest Substring

版权声明:转载我的原创博客情注明出处 https://blog.csdn.net/qq_31964727/article/details/84504167

目录

题目1 : Smallest Substring

题意分析:

1.题是什么?

2.思路

(1).大方向

(2).我的思路

(3).官方思路

(4).RMQ思路

3.ac代码

(1).我的思路

 


题目1 : Smallest Substring

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

Given a string S and an integer K, your task is to find the lexicographically smallest string T which satisfies:  

1. T is a subsequence of S

2. The length of T is K.

输入

The first line contain an integer K. (1 <= K <= 100000)

The second line contains a string of lowercase letters. The length of S is no more than 100000.

输出

The string T.

样例输入

4  
cacbbac

样例输出

abac

题意分析:

1.题是什么?

    给你一个十万长度的字符串s,和十万以内的数字k,问你长度为k的字典序最小的s的子序列是什么?

2.思路

(1).大方向

    先定义一下,ans[i]表示ans中第i个字符,slen表示原串长度,设l=ans[i-1]来源自s中的位置+1,r=slen-(k-i),ans[i]应该是选自原串s中 [l,r] 之间的最靠前的字典序最小的字符

    左边界很容易理解,ans中第i个字符自然是选自第i-1个字符所选位置之后(代码中spos就是动态记录的第i-1个字符所选位置)

    右边界是为了保证选完第i个后剩下的可选字符个数至少有k-i个,一定能凑够k个长度的答案.毕竟字典序比较是一种从左到右的贪心比较,只要这个足够小,后面的是什么无所谓,只要有那么长就够了.

(2).我的思路

    我这里第一次实现是以暴力区间搜索完成的,实际最劣复杂度其实是n*n,重复扫描了太多,故而只能过90%的数据

//tle代码
const int maxn=1e5+5;

void solve(){
	int k;
	char s[maxn];
	char ans[maxn];
	scanf("%d%s",&k,&s);
	int slen=strlen(s);
	
	int anspos=0,spos=0;
	while(anspos<k){
		char temp='z'+1;
		for(int i=spos;i<=slen-k+anspos;i++){
			if(s[i]<temp){
				temp=s[i];
				spos=i+1;
			}
		}
		ans[anspos++]=temp;
	}
	ans[anspos]='\0';
	printf("%s\n",ans);
}

    这后面我通过预处理每个字符的位置,存进vector,然后在确定ans[i]是什么时,就从'a'到'z'扫描哪个字符所对应的vector中先出现符合条件的位置.实际实现我是用数组模拟vector纯c语言实现.效率上分析其实复杂度最劣为26*maxk+maxslen,虽然名为O(n)复杂度,可是与官方提供的O(nlogn)方法相比应该更慢..毕竟logn还是很快

(3).官方思路

       官方分析:http://hihocoder.com/discuss/question/5616 

       官方思路主要是想通过维持一个小根堆或者以set平衡二叉树的性质来实现对(字符,位置)键值对的排序,以logn完成对每个无效字符的删除.并选取k个有效的最小的.

(4).RMQ思路

        其实看完大方向之后就该想到一个经典问题,RMQ区间最小值问题,不过这里的'最小'需要重新定义,这里可以以 线段树做的RMQ 做实现,

3.ac代码

(1).我的思路

#include <stdio.h>
#define maxcharnum 26
const int maxn=1e5+5;
const int inf=1e8;
char s[maxn];
char ans[maxn];

//模拟vector<int> pos[maxcharnum];
int pos[maxcharnum][maxn];
int size[maxcharnum];

void solve(){
	int k;
	scanf("%d%s",&k,&s);
	
	//初始化'vector'
	for(int i=0;i<maxcharnum;i++) size[i]=0;
	//预处理每种字符的位置进'vector',顺手把字符串长度slen做好了
	int slen=0;
	while(s[slen]){
		int temp=s[slen]-'a';
		pos[temp][size[temp]++]=slen;
		slen++;
	}
	//inf做收尾限制,必不可少
	for(int i=0;i<maxcharnum;i++) pos[i][size[i]++]=inf;
	
	int index[maxcharnum];
	for(int i=0;i<maxcharnum;i++) index[i]=0; 
	int anspos=0,spos=0;//spos记录ans[i-1]选取自s中的位置+1
	while(anspos<k){
		for(int i=0;i<maxcharnum;i++){
			while(pos[i][index[i]]<spos) index[i]++;//删除无效的位置,这就是+maxslen的来源
			if(pos[i][index[i]]>=spos&&pos[i][index[i]]<=slen-(k-anspos)){
				spos=pos[i][index[i]++]+1;
				ans[anspos]='a'+i;
				break;
			}
		}
		anspos++; 
	}
	ans[anspos]='\0';//手动结尾字符串
	printf("%s\n",ans);
}

int main(){
	solve();
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/84504167