【力扣】Z字形变换,模拟+直接构造

news2024/11/17 11:44:07

Z字形变换原题地址

方法一:利用二维矩阵模拟

对于特殊情况,z字形变换后只有一行或只有一列,则变换后的字符串和原字符串相同。

对于一般情况,我们可以考虑按照题目要求,把字符串按照Z字形存储到二维数组中,再横向遍历所有有效字符

假设Z字形变换后的矩阵有r行,字符串的长度为n。

Z字形变换是按照周期t先向下,再向右上运动。一个周期t=r+(r-2)=r*2-2

其中r-2不包含两个红圈的位置。 一个周期t内的行数为r行,列数为1+(r-2)=r-1列,即最左边的一列以及中间的r-2列。矩阵的周期数为(n/t)向上取整,即(n+t-1)/t,加上的t-1是为了向上取整。矩阵的总列数为周期数*每个周期的列数,即c=(n+t-1)/t*(r-1)

那么,什么时候向下走,什么时候向右上方走呢?这要看当前处在周期的什么位置。假设当前遍历到下标为i的字符,如果imodt<r-1,即当前处在周期的前r-1个位置,就需要向下走,否则就要向右上方走

// 方法一:利用二维矩阵模拟
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.size();
        int r = numRows; // 行数
        // 只有一行或者只有一列
        if (r == 1 || r >= n)
            return s;

        // 周期t=r+r-2
        int t = r * 2 - 2;
        // 一共有 (n/t)向上取整 个周期
        // 即(n+t-1)/t个周期
        // 每个周期有1+r-2=r-1列
        int c = (n + t - 1) / t * (r - 1); // 列数

        // 构造矩阵,即r个string的数组,每个string的长度为c
        vector<string> mat(r, string(c, 0));
        int x = 0, y = 0; // 左上角
        for (int i = 0; i < n; ++i)
        {
            mat[x][y] = s[i];
            // 周期前r-1次都是向下移动
            // 否则向右上方移动
            if (i % t < r - 1)
            {
                ++x;
            }
            else
            {
                --x;
                ++y;
            }
        }

        string ans;
        // 拼接每行有效字符
        for (auto& row : mat)
        {
            for (auto ch : row)
            {
                if (ch)
                    ans += ch;
            }
        }

        return ans;
    }
};

方法二:压缩矩阵空间

模拟时,可以不按照Z字形存储到矩阵中,而是根据当前字符在第几行,就存储在该行的最后一个位置,即尾插到当前行。这样的话可以节省矩阵的空间。

如果采用这种方案,就只需要考虑是向下走还是向上走。按照相同的思路,当imodt<r-1,即当前周期的前r-1个字符,需要向下走,反之就向上走。

// 方法二:压缩矩阵空间
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.size();
        int r = numRows; // 行数
        // 只有一行或只有一列
        if (r == 1 || r >= n)
            return s;

        vector<string> mat(r);
        // 周期t=r+r-2
        int t = r * 2 - 2;
        int x = 0; // 在第几个string后面添加字符
        for (int i = 0; i < n; ++i)
        {
            mat[x] += s[i];
            // 每个周期前r-1次向下移动
            if (i % t < r - 1)
                ++x;
            else
                --x;
        }

        string ans;
        // 拼接所有行
        for (auto& row : mat)
        {
            ans += row;
        }

        return ans;
    }
};

方法三:方法二的另一种写法

在考虑是向下走还是向上走时,可以不用计算在当前周期的第几个位置,而是直接判断当前所处位置是否在最上面还是最下面。也就是说,如果当前在第x行,若x==1或者x==r-1,说明要转向本来是向下走就要转为向上走,本来是向上走就要转为向下走

我们可以定义一个flag,如果flag=1代表向下走,flag=-1代表向上走,每次只需要x+=flag就能求出新的所在行x了。如果要转向,只需执行flag=-flag

// 方法三:方法二的另一种写法,利用flag记录何时转向
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.size();
        int r = numRows; // 行数
        // 只有一行或只有一列
        if (r == 1 || r >= n)
            return s;

        vector<string> mat(r);
        int x = 0; // 在第几个string后面添加字符
        int flag = 1; // 行转向标志,1代表向下走,-1代表向上走
        for (int i = 0; i < n; ++i)
        {
            mat[x] += s[i];
            x += flag;

            // 转向
            if (x == r - 1 || x == 0)
                flag = -flag;
        }

        string ans;
        // 拼接所有行
        for (auto& row : mat)
        {
            ans += row;
        }

        return ans;
    }
};

方法四:直接构造

前三种方法都需要构造一个新的矩阵来模拟,我们可以考虑直接构造,也就是直接取出原字符串的字符来构造ans字符串。这就需要找出Z字形变换的规律,看图:

按照“Z字形”的顺序来看,就是0->1->2->3->...->t-2->t-1->t->t+1->t+2->...->2t-2->2t-1->2t->2t+1->2t+2->...

如果我们横着看呢? 我们用i来控制行,i从0递增到r-1。再用j控制列,j从0开始,每次递增t,也就是0,t,2t,3t,...。那么下图中,每个周期都是线+方框,线是i+j,框柱的是j+t-i

对于每一行,都有线,但是第0行和第r-1行没有方框内的元素,利用这点直接构造字符串即可。

// 方法四:直接构造
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.size();
        int r = numRows; // 行数
        // 只有一行或只有一列
        if (r == 1 || r >= n)
            return s;

        string ans;
        // 周期t=r+r-2
        int t = r * 2 - 2;

        for (int i = 0; i < r; ++i)
        {
            for (int j = 0; j + i < n; j += t)
            {
                // 当前周期第一个字符
                ans += s[j + i];

                // 若不是第一行和最后一行,还有第二个字符
                if (0 < i && i < r - 1 && j + t - i < n)
                    ans += s[j + t - i];
            }
        }

        return ans;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1435624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亿级流量高并发春晚互动前端技术揭秘

前言 2022年1月&#xff0c;京东成为央视总台2022年春节联欢晚会独家互动合作伙伴&#xff0c;双方在红包互动、电商等方面展开全方位深度合作。在除夕当天产生691亿次互动&#xff0c;送出15亿元红包好物。 如何在这种大规模、高并发的场景下&#xff0c;确保系统的稳定性和…

计算机毕业设计 | SSM 医药信息管理系统(附源码)

1&#xff0c; 概述 1.1 课题背景 本系统由说书客面向广大民营药店、县区级医院、个体诊所等群体的药品和客户等信息的管理需求&#xff0c;采用SpringSpringMVCMybatisEasyui架构实现&#xff0c;为单体药店、批发企业、零售连锁企业&#xff0c;提供有针对性的信息数据管理…

【机器学习与自然语言处理】预训练 Pre-Training 各种经典方法的概念汇总

【NLP概念合集&#xff1a;一】预训练 Pre-Training&#xff0c;微调 Fine-Tuning 及其方法的概念区别 前言请看此正文预训练 Pre-Training无监督学习 unsupervised learning概念&#xff1a;标签PCA 主成分分析&#xff08;Principal Component Analysis&#xff09;降维算法L…

想要精准跟进客户,试试CRM系统!

客户跟进是任何成功企业的命脉&#xff0c;它是从初始联系到转化、从培育到购买之间的桥梁。然而&#xff0c;客户们每天都被各种信息轰炸&#xff0c;很难将注意力集中在任何一个事情上。因此&#xff0c;企业想要在客户中脱颖而出&#xff0c;就必须能够吸引并维持他们的注意…

代驾应用系统(ssm)

登录首页 管理员界面 代驾司机界面 普通用户界面 前台页面 1、系统说明 &#xff08;1&#xff09; 框架&#xff1a;spring、springmvc、mybatis、mysql、jsp &#xff08;2&#xff09; 系统分为前台系统、后端管理系统 2、欢迎留言联系交流学习讨论&#xff1a;qq 97820625…

【JS逆向学习】今日头条

逆向目标 目标网页&#xff1a;https://www.toutiao.com/?wid1707099375036目标接口&#xff1a;https://www.toutiao.com/api/pc/list/feed目标参数&#xff1a;_signature 逆向过程 老规矩先观察网络请求&#xff0c;过滤XHR请求观察加密参数&#xff0c;发现Payload的_s…

代码手术刀-自定义你的代码重构工具

前言 笔者近日在做代码仓库的存量代码缩减工作&#xff0c;首先考虑的是基于静态扫描的缩减&#xff0c;尝试使用了很多工具来对代码进行优化&#xff0c;例如PMD、IDEA自带的inspect功能、findBugs等。但是无一例外&#xff0c;要么过于“保守”&#xff0c;只给出扫描结果&a…

计算机网络自顶向下Wireshark labs-HTTP

我直接翻译并在题目下面直接下我的答案了。 1.基本HTTP GET/response交互 我们开始探索HTTP&#xff0c;方法是下载一个非常简单的HTML文件 非常短&#xff0c;并且不包含嵌入的对象。执行以下操作&#xff1a; 启动您的浏览器。启动Wireshark数据包嗅探器&#xff0c;如Wir…

HGAME 2024 WEEK1 Web方向题解 全

---------【WEEK-1】--------- Bypass it 题目描述&#xff1a;This page requires javascript to be enabled &#x1f603; 开题 不给注册&#xff0c;进注册就弹窗。根据题目描述&#xff0c;禁用JS 注册成功登录给flag 2048*16 前端小游戏出这么难。JS源码各种混淆手段…

Java-并发高频面试题-2

接着之前的Java-并发高频面试题 7. synchronized的实现原理是怎么样的&#xff1f; 首先我们要知道synchronized它是解决线程安全问题的一种方式&#xff0c;而具体是怎么解决的呢&#xff1f;主要是通过加锁的方式来解决 在底层实现上来看 是通过 monitorenter、monitorexit…

LabVIEW多功能接口卡驱动

LabVIEW多功能接口卡驱动 随着自动化测试系统的复杂性增加&#xff0c;对数据采集与处理的需求不断提高。研究基于LabVIEW开发平台&#xff0c;实现对一种通用多功能接口卡的驱动&#xff0c;以支持多通道数据采集及处理功能&#xff0c;展现LabVIEW在自动化和测量领域的强大能…

破除Github API接口的访问次数限制

破除Github API接口的访问次数限制 1、Github介绍2、Github API接口2.1 介绍2.2 使用方法 3、Github API访问限制3.1 访问限制原因3.2 访问限制类别 4、Github API访问限制破除4.1 限制破除原理4.2 限制破除示例 1、Github介绍 Github&#xff0c;是一个面向开源及私有软件项目…

opencv0014 索贝尔(sobel)算子

前面学习的滤波器主要是用来模糊图像&#xff0c;今天一起来了解关于边缘识别的滤波吧&#xff01;嘿嘿 边缘 边缘是像素值发生跃迁的位置&#xff0c;是图像的显著特征之一&#xff0c;在图像特征提取&#xff0c;对象检测&#xff0c;模式识别等方面都有重要的作用。 人眼如…

大数据企业应用场景分析

目录 一、企业分析 1.1 企业领域维度分析 1.2 技术服务型维度分析 1.3 细分领域维度分析 二、大数据应用场景 2.1 数据分析 2.2 智能推荐 2.3 产品/流程优化 2.4 异常监测 2.5 智能管理 2.6 人工智能和机器学习 三、总结 前言&#xff1a;想讲清楚大数据应用对企业…

爬虫(三)

1.JS逆向实战破解X-Bogus值 X-Bogus:以DFS开头&#xff0c;总长28位 答案是X-Bogus,因为会把负载里面所有的值打包生成X-Boogus 1.1 找X-Bogus加密位置&#xff08;请求堆栈&#xff09; 1.1.1 绝招加高级断点&#xff08;日志断点&#xff09; 日志断点看有没有X-B值 日志…

【C#】MVVM架构

示例结果展示 前提了解 MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发。 Model——可以理解为带有字段,属性的类。例如学校类,教师类,学生类等 View——可以理解为我们所看到的UI。前端界面。 View Model在View和Model之间,起到连接的作用,…

【翻译】Processing安卓模式的安装使用及打包发布(内含中文版截图)

原文链接在下面的每一章的最前面。 原文有三篇&#xff0c;译者不知道贴哪篇了&#xff0c;这篇干脆标了原创。。 译者声明&#xff1a;本文原文来自于GNU协议支持下的项目&#xff0c;具备开源二改授权&#xff0c;可翻译后公开。 文章目录 Install&#xff08;安装&#xff0…

中国学者用CHARLS数据库做横断面研究发表二区文章 IF=6.6!

欢迎报名2024年郑老师团队课程课程&#xff01; 郑老师科研统计培训&#xff0c;包括临床数据、公共数据分析课程&#xff0c;欢迎报名 2024年1月&#xff0c;中国学者在《Journal of Affective Disorders》&#xff08;二区&#xff0c;IF6.6&#xff09;发表题为&#xff1a;…

[职场] 参与秋招前你一定要知道的几件事(终篇) #其他#职场发展

参与秋招前你一定要知道的几件事&#xff08;终篇&#xff09; 39、请不要在爱好类目里填写“喜欢加班”&#xff0c;只会对hr形成10000暴击的同时会让hr产生一种不可信的印象。 40、简历命名很重要&#xff01;请务必按照既定的格式来撰写。HR在筛选时一定会有关键词&#xff…

【Docker】.NET Core 6.0 webapi 发布上传到Docker Desktop并启动运行访问,接口返回数据乱码解决方法

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…