前端算法(6)

102 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"

解题思路

函数为周期函数,如果周期为1则返回原函数,周期大于1则周期为numRows*2-2[T],在通过周期将数字分为s.length/T[K]个模块,变形后的字符串大小等同于未变形的字符串, 遍历s字符串,先计算字符所处的模块Math.Floor(i/T),然后计算字符所处块的位置,除最后一块外,前面所有块都是规律的,记录最后一块每一行字符的个数,然后统计每一行的个数,在进行动态的计算字符所处的行[0,numRows-1]返回行x,[numRows,T-1]返回T-x,当前行的的下标位置等于当前所处的块位置加上之前所有块的位置,然后计算变形后的下标即为当前行前面所有行列数量之和加上所处的位置加上当前行的位置sum(colLen)+index,最后进行返回即可

var convert = function(s, numRows) {
    let len=s.length;
    if(numRows<=1)return s;
    let nums=new Array(len);
    let t=numRows*2-2; 
    let colLen=Math.floor((len-1)/t);
    let lastArr=new Array(numRows).fill(0);
    let lastCount=(len-1)%t+1;
    for(let i=0;i<lastCount;i++){
        if(numRows-1<i){
            lastArr[2*numRows-i-2]++;
        }else{
            lastArr[i]++;
        }      
    }
    let colArr=new Array(numRows);
    for(let i=0;i<numRows;i++){
        if(i==0||i==numRows-1){
            colArr[i]=colLen+lastArr[i];
        }else{
            colArr[i]=colLen*2+lastArr[i];
        }
    }
    for(let i=0;i<len;i++){
        //计算所属模块,从0开始
        let n= Math.floor(i/t);
        //计算所属行下标
        let blockIndex=i%t;
        let rowIndex=blockIndex;
        if(numRows-1<blockIndex){
            rowIndex=numRows*2-blockIndex-2;
        }
        let index=0;
        for(let j=0;j<rowIndex;j++){
            index+=colArr[j];
        }
        //计算最后一行的位置
        if(blockIndex==0||blockIndex==numRows-1){
            index+=n+1;
        }else{
            if(numRows-1>=blockIndex){
                index+=n*2+1;
            }else{
                index+=n*2+2;
            }                  
        }       
        nums[index-1]=s[i];
    }
    return nums.join('');
};