携程2020算法--汽车时刻表分组

昨天刚做的笔试,只过了25%,当时没想懂哪里有问题,现在想明白了,改了代码发一下。

题目

时刻表:aabbcddc,字母a、b、c、d为目的地,同一个目的地的车辆必须分到同一组,车序不能打乱,对时刻表尽可能多的分组,输出各个分组的长度,逗号分隔

样例:

输入:aabbcddc

输出:2,2,4

说明:aa为一组,长度2,bb为一组,长度2,cddc为一组,长度4。因为两个c是同一个目的地,必须分到同一组。

由于这个样例坑了我,哎,所以code出错了,接下来我用另一个样例来说明:

输入:babacddc

输出:4,4

分析

对于这道题,我们要知道以下几个信息:

1、每一个字母(车站)第一次出现的顺序,样例顺序为b、a、c、d;

2、每个字母第一次出现的位置和最后一次出现的位置,样例为:

b:[0,2],  a:[1,3],  c:[4,7],  d:[5,6]

 

ok,这个时候我们就可以分割了,0~2一定要分在一组,1~3一定要分在一组,4~7一定要分在一组,5~6一定要分在一组

合并:有交集的分在一组,没有交集的分开,结果就是两组:0~3,4~7,答案输出的是长度,返回4,4。

逻辑巨简单无比,那怎么实现呢?

算法

1、map< Character,Integer>,存字母Character的出现的顺序Integer.

样例中,map={b:0,a:1,c:2,d:3}

2、lastpos数组,存字母的最后一次出现的位置,因为大小写最多就58个,所以lastpos[58]就够了(Python的话就不用定义长度啦,更方便)

#敲重点#

这里的 lastpos 是按字母顺序ABC...来存放每个字母的最后一次出现的位置吗?当然不是,怎么可能,如果这样存的话,字母的出场顺序就被拉乱,当然不允许他们乱插队啦,所以我们上面用一个map存Character出现的顺序,然后我们就可以很方便的知道,lastpos[0]代表b最后一次出现的位置,lastpos[1]代表a最后一次出现的位置,lastpos[2]代表c最后一次出现的位置,lastpos[3]代表d最后一次出现的位置

遍历的过程lastpos的变化如下:

①遍历arr[0]=b:

b加入map中,map={b:0}

lastpos:

0      

 

②遍历arr[1]到a:

b加入map中,map={b:0,a:1}

lastpos:

0 1    

 

③遍历arr[2]到b:

map={b:0,a:1}

lastpos[map.get('b')]=lastpos[0]=2:

2 1    

 

④遍历arr[3]到a:

map={b:0,a:1}

lastpos[map.get('a')]=lastpos[1]=3:

2 3    

 

⑤遍历arr[4]到c:

c加入map中,map={b:0,a:1,c:2}

lastpos[map.get('c')]=lastpos[2]=4:

2 3 4  

 

...

继续直到遍历完,后面就不写出来了,最后:

map={b:0,a:1,c:2,d:3}

lastpos:

2 3 7 6

 

3、合并:

啊啊,这个也是重点,我昨天的笔试就是错在了这里!!!ok,fine,不重要,继续讲。

其实,与其说是合并区间,不如说是区间右扩,哈哈哈,看过左神的课的是不是有点懂懂的呢~没错,只右扩(连左缩都没有哦),所以复杂度是O(n)的。具体怎么做呢?来,前排小板凳。

再一次遍历输入的字母序列(代码中的input[]),

①遍历input[0]=b:

map.get['b'] = 0,所以在lastpos[0] 中存放着b最后一次出现的位置为2,ok,用一个max存放,我这个分组的区间,最少要到2。

②遍历input[1]=a:

map.get['a'] = 1,所以在lastpos[a] 中存放着a最后一次出现的位置为3,#重点来了#,这个时候,区间要右扩,max=3

为什么呢?本来max=2,我至少要把0~2分为一组,才能保证位置0的b和位置2的b分到同一组,这个时候,第一个分组至少要包含所有的字母b,现在位置1多了一个a,那个我第一个分组就至少应该包含所有的a、b字母,而a最后结束的位置是3,所以位置3的a也应该在这个组中,所以,右扩咯~max=3.

③遍历input[2]=b: max=3,继续

④遍历input[3]=a: max=3,这个时候,遍历的位置3(遍历到的input的位置) 和max的位置是一样的,证明什么?前面的区间里的字母,都是纯纯的啦,后面再也没有前面出现的字母哈哈哈,太棒了,那么我就可以输出了,第一个区间是[0,3],那么长度就是4咯~

记录一下,last = 3, [0,3]的区间之后我就可以不用管了~~现在的max应该是什么,恩,下一个字母c最后出现的位置7,so,max=7.

⑤遍历input[4]=c:max=7,继续...

一直继续到结束就完事了~不要忘记输出间隔符‘,’哦~

最后结果就是:4,4

代码如下:

import java.util.HashMap;
import java.util.Scanner;

public class Main {
	public static void main(String args[]) {
		Scanner sc = new Scanner(System.in);
                //输入的字母序列
		char[] input= sc.nextLine().toCharArray();
		HashMap< Character,Integer> map = new HashMap();
		int[] lastpos = new int[58];
                //index作为标记,代表下一个新出现的字母是第几个出现的,从0开始
		int index = 0;
                //map和lastpos是同时更新的
		for(int i=0;i

优化

呃,是有个聊胜于无的优化方案的,就是当第一次碰到input的长度,即max = input.length-1时,就可以不用继续遍历了,因为他一定是最后一个区间了,都到最末了,输出该区间长度就可以pass了。不过我上面没写,更新max后加一个判断就可以了。因为时间复杂度不变,时间未必会短,甚至可能会变长,要看数据哈哈哈哈哈。

补充

突然想到一个很蠢的事情,如果觉得用lastpos数组,要转换下标和字符的对应关系很麻烦怕出错,其实用两个map,一个存字母第一次出现的位置,一个存字母最后一次出现的位置,也是可以的,而且逻辑清晰很多,代码我就不改了,小伙伴可以自己实现哦~

你可能感兴趣的:(携程2020算法--汽车时刻表分组)