标签: 模拟

LeetCode刷题【1518】换酒问题

小区便利店正在促销,用 numExchange 个空酒瓶可以兑换一瓶新酒。你购入了 numBottles 瓶酒。

如果喝掉了酒瓶中的酒,那么酒瓶就会变成空的。

请你计算 最多 能喝到多少瓶酒。

 

示例 1:

输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 9 + 3 + 1 = 13 瓶酒。

示例 2:

输入:numBottles = 15, numExchange = 4
输出:19
解释:你可以用 4 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 15 + 3 + 1 = 19 瓶酒。

示例 3:

输入:numBottles = 5, numExchange = 5
输出:6

示例 4:

输入:numBottles = 2, numExchange = 3
输出:2

 

提示:

  • 1 <= numBottles <= 100
  • 2 <= numExchange <= 100
Related Topics
  • 数学
  • 模拟

  • 👍 140
  • 👎 0
  • 模拟,记得一次换不了的剩余空瓶可以攒着一起换
    没啥好说的,直接模拟,需要有一天个注意的地方,我们需要记录下没被换掉的剩余的空瓶

    可以几次攒下来不够换的空瓶,攒够了numExchange个后来换

    所以

    1. 初始我们买了numBottles瓶,喝了这么多之后就有这么多空瓶
    2. 第一次换酒剩余了left = numBottles%numExchange;瓶无法兑换
    3. 第一次换酒换到了numBottles /= numExchange;瓶,即喝到的瓶数增加了这么多
    4. 那么此时手中剩余空瓶数量为numBottles + left瓶空瓶,重新回到步骤1开始模拟

    代码

    class Solution {
        public int numWaterBottles(int numBottles, int numExchange) {
            int ans = numBottles;
            int left = 0;
            while (numBottles >= numExchange){
                left = numBottles%numExchange;
                numBottles /= numExchange;
                ans += numBottles;
                numBottles += left;
            }
            return ans;
        }
    }

    LeetCode刷题【289】生命游戏

    根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。

    给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

    1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
    2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
    3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
    4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

    下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

     

    示例 1:

    输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
    输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
    

    示例 2:

    输入:board = [[1,1],[1,0]]
    输出:[[1,1],[1,1]]
    

     

    提示:

    • m == board.length
    • n == board[i].length
    • 1 <= m, n <= 25
    • board[i][j]01

     

    进阶:

    • 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
    • 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
    Related Topics
    • 数组
    • 矩阵
    • 模拟

  • 👍 438
  • 👎 0
  • 数组复制 & 简化易懂版的原地修改
    题意很好理解,直接上来就是淦代码

    顺便自己写了个页面http://next.cheungq.com/ 里面有个生命游戏的演示

    数组复制的方法代码

    class Solution {
        public void gameOfLife(int[][] board) {
            int m = board.length;
            int n = board[0].length;
            int[][] ans = new int[m][n];
            int[][] dir = new int[][]{{1,0},{-1,0},{0,1},{0,-1},{1,-1},{1,1},{-1,1},{-1,-1}};
    
            for (int row = 0; row < m; row++) {
                for (int col = 0; col < n; col++) {
                    int cnt = 0;
                    for (int i = 0; i < dir.length; i++) {
                        int x = row+dir[i][0];
                        int y = col+dir[i][1];
                        if (x<0||y<0||x>=m||y>=n){
                            continue;
                        }
                        cnt += board[x][y];
                    }
    
                    if (board[row][col] == 1){
                        //如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
                        ans[row][col] = (cnt == 2 || cnt == 3)?1:0;
                        //如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
                        //如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
                    }else{
                        //如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
                        ans[row][col] = cnt==3?1:0;
                    }
                }
            }
    
            for (int row = 0; row < m; row++) {
                for (int col = 0; col < n; col++) {
                    board[row][col] = ans[row][col];
                }
            }
        }
    }

    原地修改

    而原地修改大佬们用的方法略高端,用二进制位的状态来标记。我这有个简化版的原理一样。
    思路如下,也许可以帮到你更加容易的来理解这个过程

    1. 因为周围最多只有8个格子,所以周围最多活细胞数量只有8个,这个数字小于10,那么我们可以活用这个关系
    2. 讲当前格子状态进一位,变成10或者00,第一位表示当前格子的状态
    3. 后面一位的0我们更新为周围8个格子的活细胞数量,
    4. 比如更新后当前格子的值为14,第一位1表示当前格子是活细胞,周围8个格子中有4个活细胞
    5. 又比如更新后当前格子的值为5,第一位的10位数上没有,即为0,那么表明当前格子为死细胞,周围有5个活细胞
    6. 一遍更新完了之后,我们就可以根据更新后的结果值来重新换算下当前格子的下一步状态值是死细胞还是活细胞了
    7. 又,因为是从左往右,从上往下的更新的,所以,在计算周围8个格子中活细胞数量的时候需要注意一下以下情况
      • 当前行的上一行对应的3个格子中的结果是已经计算过之后的结果,所以判断细胞死活情况需要判断的是10位数的值
      • 当前格子当前行左边一个格子也是已经计算过的结果,所以也需要根据10位数的值来判断死活情况

    原地修改代码

    class Solution {
        public void gameOfLife(int[][] board) {
            int m = board.length;
            int n = board[0].length;
            int[][] dir = new int[][]{{1,0},{-1,0},{0,1},{0,-1},{1,-1},{1,1},{-1,1},{-1,-1}};
            int cnt = 0;
            for (int row = 0; row < m; row++) {
                for (int col = 0; col < n; col++) {
                    cnt = 0;
                    for (int i = 0; i < dir.length; i++) {
                        int x = row+dir[i][0];
                        int y = col+dir[i][1];
                        if (x<0||y<0||x>=m||y>=n){
                            continue;
                        }
                        if (dir[i][0]<0 || (dir[i][0]==0 && dir[i][1]<0)){
                            cnt += board[x][y]/10;
                        }else{
                            cnt += board[x][y];
                        }
                    }
                    board[row][col] = board[row][col]*10 + cnt;
                }
            }
    
            for (int row = 0; row < m; row++) {
                for (int col = 0; col < n; col++) {
                    cnt = board[row][col] % 10;
                    int status = board[row][col]/10;
                    if (status==1){
                        board[row][col] = (cnt == 2 || cnt == 3)?1:0;
                    }else{
                        board[row][col] =  cnt==3?1:0;
                    }
                }
            }
        }
    }

    LeetCode刷题【1409】查询带键的排列

    给你一个待查数组 queries ,数组中的元素为 1m 之间的正整数。 请你根据以下规则处理所有待查项 queries[i](从 i=0i=queries.length-1):

    • 一开始,排列 P=[1,2,3,...,m]
    • 对于当前的 i ,请你找出待查项 queries[i] 在排列 P 中的位置(下标从 0 开始),然后将其从原位置移动到排列 P 的起始位置(即下标为 0 处)。注意, queries[i]P 中的位置就是 queries[i] 的查询结果。

    请你以数组形式返回待查数组  queries 的查询结果。

     

    示例 1:

    输入:queries = [3,1,2,1], m = 5
    输出:[2,1,2,1] 
    解释:待查数组 queries 处理如下:
    对于 i=0: queries[i]=3, P=[1,2,3,4,5], 3 在 P 中的位置是 2,接着我们把 3 移动到 P 的起始位置,得到 P=[3,1,2,4,5] 。
    对于 i=1: queries[i]=1, P=[3,1,2,4,5], 1 在 P 中的位置是 1,接着我们把 1 移动到 P 的起始位置,得到 P=[1,3,2,4,5] 。 
    对于 i=2: queries[i]=2, P=[1,3,2,4,5], 2 在 P 中的位置是 2,接着我们把 2 移动到 P 的起始位置,得到 P=[2,1,3,4,5] 。
    对于 i=3: queries[i]=1, P=[2,1,3,4,5], 1 在 P 中的位置是 1,接着我们把 1 移动到 P 的起始位置,得到 P=[1,2,3,4,5] 。 
    因此,返回的结果数组为 [2,1,2,1] 。  
    

    示例 2:

    输入:queries = [4,1,2,2], m = 4
    输出:[3,1,2,0]
    

    示例 3:

    输入:queries = [7,5,5,8,3], m = 8
    输出:[6,5,0,7,5]
    

     

    提示:

    • 1 <= m <= 10^3
    • 1 <= queries.length <= m
    • 1 <= queries[i] <= m
    Related Topics
    • 树状数组
    • 数组
    • 模拟

  • 👍 39
  • 👎 0
  • 树状数组实现
    思路同官方题解

    1. 树状数组的长度为queries.length + m,把原先的m序列在树状数组中后面部分构件起来
    2. 这样移动某个数字到前面的时候,就等于把树状数组后面的这个数字,往前面部分移动,需要在树状数组中对后面的key进行减1,并在前面移动到的新位置加1
    3. 这样需要维护一个数组记录每个数字当前所在的索引下标,移动到前面的时候更新索引下标为前面的值
    4. 每次查询当前位置前面的前缀和即为前面有多少个数字,并需要减1,因为包含了当前数字
    class Solution {
        public int[] processQueries(int[] queries, int m) {
            int l = queries.length + m;
            FenwickTree fenwickTree = new FenwickTree(l);
            int[] numPos = new int[m+1];
            for (int i = queries.length + 1; i <= l; i++) {
                fenwickTree.add(i,1);
                numPos[i - queries.length] = i;
            }
    
            int[] res = new int[queries.length];
            for (int i = 0; i < queries.length; i++) {
                int num = queries[i];
                res[i] = fenwickTree.query(numPos[num]) - 1;
                fenwickTree.add(numPos[num],-1);
                numPos[num] = queries.length - i;
                fenwickTree.add(numPos[num],1);
            }
            return res;
        }
    
        class FenwickTree{
            int[] arr;
    
            public FenwickTree(int n){
                arr = new int[n+1];
            }
    
            public int lowBit(int i){
                return i & (-i);
            }
    
            public void add(int idx , int num){
                while (idx < arr.length){
                    arr[idx] += num;
                    idx += lowBit(idx);
                }
            }
    
            public int query(int idx){
                int sum = 0;
                while (idx > 0){
                    sum += arr[idx];
                    idx -= lowBit(idx);
                }
                return sum;
            }
    
        }
    }

    LeetCode刷题【2069】模拟行走机器人 II

    给你一个在 XY 平面上的 width x height 的网格图,左下角 的格子为 (0, 0) ,右上角 的格子为 (width - 1, height - 1) 。网格图中相邻格子为四个基本方向之一("North""East""South" 和 "West")。一个机器人 初始 在格子 (0, 0) ,方向为 "East" 。

    机器人可以根据指令移动指定的 步数 。每一步,它可以执行以下操作。

    1. 沿着当前方向尝试 往前一步 。
    2. 如果机器人下一步将到达的格子 超出了边界 ,机器人会 逆时针 转 90 度,然后再尝试往前一步。

    如果机器人完成了指令要求的移动步数,它将停止移动并等待下一个指令。

    请你实现 Robot 类:

    • Robot(int width, int height) 初始化一个 width x height 的网格图,机器人初始在 (0, 0) ,方向朝 "East" 。
    • void move(int num) 给机器人下达前进 num 步的指令。
    • int[] getPos() 返回机器人当前所处的格子位置,用一个长度为 2 的数组 [x, y] 表示。
    • String getDir() 返回当前机器人的朝向,为 "North" ,"East" ,"South" 或者 "West" 。

     

    示例 1:

    输入:
    ["Robot", "move", "move", "getPos", "getDir", "move", "move", "move", "getPos", "getDir"]
    [[6, 3], [2], [2], [], [], [2], [1], [4], [], []]
    输出:
    [null, null, null, [4, 0], "East", null, null, null, [1, 2], "West"]
    
    解释:
    Robot robot = new Robot(6, 3); // 初始化网格图,机器人在 (0, 0) ,朝东。
    robot.move(2);  // 机器人朝东移动 2 步,到达 (2, 0) ,并朝东。
    robot.move(2);  // 机器人朝东移动 2 步,到达 (4, 0) ,并朝东。
    robot.getPos(); // 返回 [4, 0]
    robot.getDir(); // 返回 "East"
    robot.move(2);  // 朝东移动 1 步到达 (5, 0) ,并朝东。
                    // 下一步继续往东移动将出界,所以逆时针转变方向朝北。
                    // 然后,往北移动 1 步到达 (5, 1) ,并朝北。
    robot.move(1);  // 朝北移动 1 步到达 (5, 2) ,并朝  (不是朝西)。
    robot.move(4);  // 下一步继续往北移动将出界,所以逆时针转变方向朝西。
                    // 然后,移动 4 步到 (1, 2) ,并朝西。
    robot.getPos(); // 返回 [1, 2]
    robot.getDir(); // 返回 "West"
    
    

     

    提示:

    • 2 <= width, height <= 100
    • 1 <= num <= 105
    • move ,getPos 和 getDir 总共 调用次数不超过 104 次。
    Related Topics
  • 设计
  • 模拟

  • 👍 17
  • 👎 0
  • JAVA模拟

    模拟得有点费劲

    1. 调用move()方法的时候并不去实际模拟走动,而是增加步数并取模
    2. 调用getPos()getDir()的时候才去模拟走动
    3. 初始direction定义为SOUTH,后面取模为0的话不用另算
    4. 但是初始定为SOUTH初始查询方向的时候就不对了,所以再加一个moved判断,只要执行过move()就返回真实的direction,没执行过moved的话,就返回向东

    可能还是数学计算的方法简单点吧,

    class Robot {
    
        int[][] matrix;
    
        private static String NORTH = "North";
        private static String EAST = "East";
        private static String SOUTH = "South";
        private static String WEST = "West";
    
        private int[] position;
        private static String[] dirArr;
        {
            dirArr = new String[]{ EAST, NORTH,  WEST, SOUTH };
        }
    
        private int direction = 3;
    
        private int perimeter = 0;
    
        private int count = 0;
    
        boolean moved = false;
    
        public Robot(int width, int height) {
            matrix = new int[width][height];
            position = new int[2];
            perimeter = (width * 2 + height * 2) - 4;
        }
    
        public void move(int num) {
            count += num;
            count %= perimeter;
            moved = true;
        }
    
    
        /**
         *
         *        西 ↑
         *   南         北
         *   ←          →
         *        东 ↓
         *
         */
        private void goNext(){
            int[] next;
            switch (direction){
                case 0:
                    next = new int[]{position[0]+1,position[1]};
                    break;
                case 1:
                    next = new int[]{position[0],position[1]+1};
                    break;
                case 2:
                    next = new int[]{position[0]-1,position[1]};
                    break;
                case 3:
                    next = new int[]{position[0],position[1]-1};
                    break;
                default:
                    return;
            }
            if (next[0] >= matrix.length || next[1] >= matrix[0].length || next[0]<0 ||next[1] <0){
                direction = (direction+1)%4;
                goNext();
                return ;
            }
            position = next;
        }
    
    
        private void __move(){
            while (count>0){
                goNext();
                count--;
            }
        }
    
        public int[] getPos() {
            __move();
            return position;
        }
    
        public String getDir() {
            __move();
            return moved?dirArr[direction]:dirArr[0];
        }
    }

    LeetCode刷题【495】提莫攻击

    在《英雄联盟》的世界中,有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。

    当提莫攻击艾希,艾希的中毒状态正好持续 duration 秒。

    正式地讲,提莫在 t 发起发起攻击意味着艾希在时间区间 [t, t + duration - 1](含 tt + duration - 1)处于中毒状态。如果提莫在中毒影响结束 再次攻击,中毒状态计时器将会 重置 ,在新的攻击之后,中毒影响将会在 duration 秒后结束。

    给你一个 非递减 的整数数组 timeSeries ,其中 timeSeries[i] 表示提莫在 timeSeries[i] 秒时对艾希发起攻击,以及一个表示中毒持续时间的整数 duration

    返回艾希处于中毒状态的 秒数。

     

    示例 1:

    输入:timeSeries = [1,4], duration = 2
    输出:4
    解释:提莫攻击对艾希的影响如下:
    - 第 1 秒,提莫攻击艾希并使其立即中毒。中毒状态会维持 2 秒,即第 1 秒和第 2 秒。
    - 第 4 秒,提莫再次攻击艾希,艾希中毒状态又持续 2 秒,即第 4 秒和第 5 秒。
    艾希在第 1、2、4、5 秒处于中毒状态,所以总中毒秒数是 4 。

    示例 2:

    输入:timeSeries = [1,2], duration = 2
    输出:3
    解释:提莫攻击对艾希的影响如下:
    - 第 1 秒,提莫攻击艾希并使其立即中毒。中毒状态会维持 2 秒,即第 1 秒和第 2 秒。
    - 第 2 秒,提莫再次攻击艾希,并重置中毒计时器,艾希中毒状态需要持续 2 秒,即第 2 秒和第 3 秒。
    艾希在第 1、2、3 秒处于中毒状态,所以总中毒秒数是 3 。
    

     

    提示:

    • 1 <= timeSeries.length <= 104
    • 0 <= timeSeries[i], duration <= 107
    • timeSeries非递减 顺序排列
    Related Topics
  • 数组
  • 模拟

  • 👍 302
  • 👎 0
  • 模拟分析一下【 本次的结束时间 – max ( 本次开始时间, 上次结束时间 ) 】

    两种情况如下见图

    1. 当前的结束时间在下次的开始时间之后,中间有重复部分,需要减去
    2. 当前的结束时间在下次的开始时间之前,只需加上对应的duration即可
     0 1 2 3 4 5 6 7 8 9 10
     - - -
         - - -
                 - - -
    

    综合两种情况的分析,需要加上的时长为

    本次的结束时间 - max( 本次开始时间, 上次结束时间 )
    class Solution {
        public int findPoisonedDuration(int[] timeSeries, int duration) {
            int ans = 0;
            int last = timeSeries[0];
            for (int timeSery : timeSeries) {
                ans += timeSery + duration - Math.max(last,timeSery);
                last = timeSery + duration;
            }
            return ans;
        }
    }

    LeetCode刷题【剑指 Offer 31】栈的压入、弹出序列

    输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

     

    示例 1:

    输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
    输出:true
    解释:我们可以按以下顺序执行:
    push(1), push(2), push(3), push(4), pop() -> 4,
    push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
    

    示例 2:

    输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
    输出:false
    解释:1 不能在 2 之前弹出。
    

     

    提示:

    1. 0 <= pushed.length == popped.length <= 1000
    2. 0 <= pushed[i], popped[i] < 1000
    3. pushed 是 popped 的排列。

    注意:本题与主站 946 题相同:https://leetcode-cn.com/problems/validate-stack-sequences/

    Related Topics
  • 数组
  • 模拟

  • 👍 324
  • 👎 0
  • Java 栈模拟,分步分析理解

    直接模拟

    1. 构造一个Stack,以及两个下标位置标记idxPopidxPush
    2. 一开始为空,先压入一个pushed
    3. 判断栈顶元素是否和popped当前下标的的值一样,如果一样,则说明当前应该进行弹出操作,给stack.pop();同时idxPop++;
    4. 持续执行步骤3,直到不需要弹出为止
    5. 再次回到步骤2,继续往栈内压入元素,记得同时idxPush++,标记执行到哪一步了
    6. 步骤2至5是是一个完整准确的栈压入、弹出过程、只要按照这个步骤就可以顺利执行到结束
    7. 最终判断idxPopidxPush这两个指针是否都指向了poppedpushed两个数组的结尾,如果都指向了结尾则表明所有数据都进行过了压入栈操作,同时所有数据都进行了弹出栈操作。
    class Solution {
        public boolean validateStackSequences(int[] pushed, int[] popped) {
            Stack<Integer> stack = new Stack<>();
            int idxPop = 0;
            int idxPush = -1;
            while ( ++idxPush < pushed.length){
                stack.add(pushed[idxPush]);
                while (!stack.isEmpty() && stack.peek() == popped[idxPop]){
                    idxPop++;
                    stack.pop();
                }
            }
            return idxPush == pushed.length && idxPop == popped.length;
        }
    }

    LeetCode刷题【38】外观数列

    给定一个正整数 n ,输出外观数列的第 n 项。

    「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

    你可以将其视作是由递归公式定义的数字字符串序列:

    • countAndSay(1) = "1"
    • countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

    前五项如下:

    1.     1
    2.     11
    3.     21
    4.     1211
    5.     111221
    第一项是数字 1 
    描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
    描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
    描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
    描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
    

    描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

    例如,数字字符串 "3322251" 的描述如下图:

     

    示例 1:

    输入:n = 1
    输出:"1"
    解释:这是一个基本样例。
    

    示例 2:

    输入:n = 4
    输出:"1211"
    解释:
    countAndSay(1) = "1"
    countAndSay(2) = 读 "1" = 一 个 1 = "11"
    countAndSay(3) = 读 "11" = 二 个 1 = "21"
    countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"
    

     

    提示:

    • 1 <= n <= 30
    Related Topics
  • 字符串

  • 👍 765
  • 👎 0
  • 字符串模拟,这种阅读起来好像还是比直接sb拼接费劲不少。岂止费劲,还看得眼花。
    逻辑也是一样的,

    • 从’1’开始
    • 2个’1′ 就是 “21”
    • 然后“21”就是 1个’2’、1个’1’即“1211”
    • “1211”就是1个’1’、1个’2’、2个’1’,即“111221”
    • 后面依次类推

    class Solution {
        public String countAndSay(int n) {
            if (n == 1){
                return "1";
            }
            char[][] arr = new char[2][4500];
            arr[0][0] = '1';
            int i = 2;
            int cur = 1;
            int pre = 0;
            int preIdx = 0;
            int curIdx = 0;
            while (i <= n){
                cur = (i-1) % 2;
                pre = i % 2;
                preIdx = 0;
                curIdx = -1;
                while (arr[pre][preIdx] != '\u0000'){
                    char numChar = arr[pre][preIdx];
                    int count = 1;
                    while (arr[pre][preIdx] == arr[pre][preIdx+1]){
                        count++;
                        preIdx++;
                    }
                    preIdx++;
                    if (count > 10){
                        int countStringSize = 1;
                        int x = 10;
                        while (count/x != 0){
                            x *= 10;
                            countStringSize++;
                        }
                        curIdx++;
                        while (--countStringSize>=0){
                            arr[cur][curIdx + countStringSize] = (char)('0'+ count%10);
                            count = count / 10;
                        }
                        curIdx += countStringSize-1;
                    }else{
                        arr[cur][++curIdx] = (char)('0' + count);
                    }
                    arr[cur][++curIdx] = numChar;
                }
                i++;
            }
            char[] strChars = new char[curIdx+1];
            System.arraycopy(arr[cur],0,strChars,0,curIdx+1);
            return new String(strChars);
        }
    }