Leetcode 2020//11/30打卡 767. 重构字符串(中等)

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"
输出: "aba"
示例 2:

输入: S = "aaab"
输出: ""
注意:
S 只包含小写字母并且长度在[1, 500]区间内

看到长度在【1,500】之间的时候,就嘿嘿了,不会存在超时的风险
官方题解:就一句:

对于长度为 nn 的字符串,每次从最大堆取出两个字母的操作

就很官方,上来就是Priorityqueue(可以参考https://blog.csdn.net/u010623927/article/details/87179364了解一下)
主要用到offer()==就是add()和poll()就是pop():

class Solution {
    public String reorganizeString(String S) {
        if (S.length() < 2) {
            return S;
        }
        //首先定义计数数组,字母26都是这个套路了
        int[] counts = new int[26];
        int maxCount = 0;
        int length = S.length();
        for (int i = 0; i < length; i++) {
            char c = S.charAt(i);
            counts[c - 'a']++;
            //顺便记录一下最大值
            maxCount = Math.max(maxCount, counts[c - 'a']);
        }
        //最大个数如果超过一半,比如len=8, maxCount=5, 那么剩下的3个数字是无法隔开5个数的
        //这里的length+1将奇数和偶数的情况合二为一
        //偶数:if((len & 1) == 0 && max > len/2) { return "";}
        //奇数:if((len & 1) == 1 && max > len/2 + 1) {return "";}
        if (maxCount > (length + 1) / 2) {
            return "";
        }
        //创建PriorityQueue,重写排序规则,模板啊
        PriorityQueue<Character> queue = new PriorityQueue<Character>(new Comparator<Character>() {
            public int compare(Character letter1, Character letter2) {
                return counts[letter2 - 'a'] - counts[letter1 - 'a'];
            }
        });
        for (char c = 'a'; c <= 'z'; c++) {
            if (counts[c - 'a'] > 0) {
                queue.offer(c);
            }
        }
        StringBuffer sb = new StringBuffer();
        //每次从队列里取出两个值拼接,肯定不是重复值的了,然后计数分别减一之后再放回去
        //如果下回取出来的还是这两个数,还是符合题意的,比如ab + ab
        //什么条件是>1而不是>0,因为每回要取出两个数,这种情况下,最后会剩下0或者1个数
        //最后处理剩下的那个数
        while (queue.size() > 1) {
            char letter1 = queue.poll();
            char letter2 = queue.poll();
            sb.append(letter1);
            sb.append(letter2);
            int index1 = letter1 - 'a', index2 = letter2 - 'a';
            counts[index1]--;
            counts[index2]--;
            if (counts[index1] > 0) { //这里的小判断,计数为0,就是没有了,就不再考虑这个字符了
                queue.offer(letter1);
            }
            if (counts[index2] > 0) {
                queue.offer(letter2);
            }
        }
        //处理剩下的那个数
        if (queue.size() > 0) {
            sb.append(queue.poll());
        }
        return sb.toString();
    }
} 

因为官方题解用到了特别的数据结构,如果面试官冷冷的说:如果不用优先队列(PriorityQueue)如何处理呢:
java 插空法横空出世了(我自己的代码就是这思路,我也是看了别人的题解,才明白这是插空法)

将数量最多的依次排开,其余的插空即可

class Solution {
    public String reorganizeString(String S) {
        char[] chs = S.toCharArray();
        //刚刚学习了一种插值法,试试
        //一样的套路,计数是躲不过去了的
        int[]arr = new int[26];
        int max = -1, idx = -1;
        for(char c:chs){
            arr[c-'a']++;
            if(max < arr[c-'a']){
                max = arr[c-'a'];
                idx = c-'a';
            }
        } 
        //判断一下最大个数
        if(max > (chs.length+1)/2) return ""; 
        
        StringBuffer sb = new StringBuffer();
        //首先把最多的字符放进来,其他字符在此基础上插进来
        while(arr[idx]>0){
            sb.append((char)(idx+'a'));
            arr[idx]--; //注意计数的变化
        }  
        //接下来,就用我们亲爱的position记录插入的位置了
        //个人认为这是精华
        //position初始化为1,比如sb插入玩最多的字符后为aaaa,
        //首先在1的位置上插入b,成为abaaa,=请注意sb是时刻变化的
        //那么再插入时,就该在位置为3的地方了,aba'b'aaa,注意引号中字符的位置
        //这是跳跃着进行插入的....
        //当然如果position到最后了,position最大值是sb的length,需要让positon回到初始的地方position=1接着跳跃
        int position = 1;
        for(int i=0;i<arr.length;++i){
            while(arr[i]>0){
                sb.insert(position, (char)(i+'a'));
                position += 2;//让position跳跃着
                arr[i]-=1;
                if(position > sb.length()) position = 1; //回到初始的地方
            }
        }
        return sb.toString();
    }
}

你可能感兴趣的:(Leetcode打卡,leetcode,算法)