JYT's Leetcode Solution(3)

1 题目描述

题14 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串””。

示例1

1
2
输入:strs = ["flower","flow","flight"]
输出:"fl"

示例2

1
2
3
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

2 问题解法

2.1 显而易见的办法

乍一看,我们很容易想到比较字符串数组中各字符串的第一个字符,若全部相同,再比较第二个,以此类推。显然,此解法的时间复杂度为O(n^2)。经过测试,部分样例超出时间限制。

2.2 仔细想想

在大部分编程语言中,不仅数值型数据可以比较大小,字符串也可以比较大小。比较大小时,计算机会从前往后依次比较字符串各个字符ASCLL码值的大小,直至出现不等。那么,一个字符串数组中最大的与最小的字符串之间有什么关系呢?在示例1中,最大字符串为flower,最小字符串为flight,它们的最长公共前缀fl即为整个数组的最长公共前缀。这样的规律是普遍的。因此,我们可以先求出字符串数组的最大与最小的字符串,然后求出它们的最长公共前缀,,从而得到所求结果。此算法的时间复杂度只有O(n)。

3 求解代码

  • c++代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Solution {
    public:
    string longestCommonPrefix(vector<string>& strs) {
    int num = strs.size();
    //求字符串数组中最大与最小的字符串
    string maxs = strs[0];
    string mins = strs[0];
    for(int i = 1; i < num; i++){
    if(strs[i] > maxs){
    maxs = strs[i];
    }
    if(strs[i] < mins){
    mins = strs[i];
    }
    }
    //求两字符串的最长公共前缀
    string re = "";
    int pos = 0;
    while(pos < maxs.length() && pos < mins.length()){
    if(maxs[pos] == mins[pos]){
    re += maxs[pos];
    pos++;
    }else{
    break;
    }
    }
    return re;
    }
    };
  • python代码
    1
    2
    3
    4
    5
    6
    7
    8
    class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
    s1 = min(strs)
    s2 = max(strs)
    for i,x in enumerate(s1):
    if x != s2[i]:
    return s2[:i]
    return s1

4 总结

找出规律,复杂度O(n^2)的算法可以化简到O(n)。

JYT's Leetcode Solution(2)

1 题目描述

题6 Z字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:

1
2
3
P   A   H   N
A P L S I I G
Y I R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:”PAHNAPLSIIGYIR”。请你实现这个将字符串进行指定行数变换的函数:

示例1

1
2
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例2

1
2
3
4
5
6
7
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I

示例3

1
2
输入:s = "A", numRows = 1
输出:"A"

2 问题解法

2.1 暴力求解

(此方法是最愚蠢的方法,首次提交执行用时84ms,内存消耗93MB)

初次见到该题,我们很容易被题目中关于“Z字形”的描述所欺骗。按问题所述,很容易联想到创建一个二维矩阵,然后将待变换字符串每一个字符按要求依次填入该矩阵中(空位用空格字符表示),最后逐行输出结果。显然,此解法的时间复杂度为O(n^2)。

2.2 转换思路

那么,有没有时间复杂度为O(n)的算法呢?答案是显然的。在暴力求解的过程中,我们发现,矩阵每一行字符的前后顺序一定与原字符串的顺序相同,而且最终字符串由矩阵逐行输出而得。因此,我们将舍弃二维矩阵,在c++中为二维字符数组,转而使用一维字符串数组。当我们确认原字符串每一位字符所在的行数时,直接接在数组对应字符串元素之后即可。最后,我们将各字符串逐行连接,即可得到正确结果。

3 求解代码

  • 代码行数较少,无注释
  • c++代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Solution {
    public:
    string convert(string s, int numRows) {
    if(numRows == 1){
    return s;
    }
    int l = s.length();
    int blockSize = 2 * (numRows - 1);
    string ss[numRows];
    for(int i = 0; i < numRows; i++){
    ss[i] = "";
    }
    for(int i = 0; i < l; i++){
    int loc = i % blockSize;
    if(loc < numRows){
    ss[loc] += s[i];
    }else{
    ss[blockSize - loc] += s[i];
    }
    }
    string sRet = "";
    for(int i = 0; i < numRows; i++){
    sRet += ss[i];
    }
    return sRet;
    }
    };
  • python代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Solution:
    def convert(self, s: str, numRows: int) -> str:
    if numRows == 1:
    return s
    else:
    blockSize = 2 * (numRows - 1)
    ss = []
    for i in range(numRows):
    ss.append("")
    for i in range(len(s)):
    loc = i % blockSize
    if loc < numRows:
    ss[loc] += s[i]
    else:
    ss[blockSize - loc] += s[i]
    sRet = ""
    for i in range(numRows):
    sRet += ss[i]
    return sRet

4 总结

有时候,优秀的解法并不能直接想出,从最基本的开始做起也不错。

JYT's First Post

1 简介

这是JYT的第一篇hexo博客,主要目的是熟悉markdown语言的语法以及hexo博客的发布。主要内容是分享leetcode上一道执行时间0ms的c++程序题解。

2 问题与题解

2.1 问题描述

给你一个字符串 s,找到 s 中最长的回文子串。

示例1

1
2
输入:s = "babad"
输出:"bab"

示例2

1
2
输入:s = "cbbd"
输出:"bb"

2.2 算法思想

  • 1 动态规划算法

若字符串的某一子串是回文的,那么当其去掉首尾两字符后所得的新字串仍是回文的。该算法逻辑较为复杂,本题采用中心扩展算法。

  • 2 中心扩展算法

先从较短的回文子串开始,例如”babad”的”a”或者”cbbd”的”bb”,标记该子串的首尾位置并分别向前后进行遍历,若首位置前一字符与尾位置后一字符相等,则移动首尾位置,直至出现不等。较短子串可能为单字符或者连续多字符,需分别讨论。遍历时需注意边界情况,即首尾位置不能超出字符串的范围。最后计算子串长度,并与最长子串长度进行比较。返回时,使用c++字符串分割函数substr(),参数为最长子串的首位置与最长子串长度。

  • 3 特殊讨论

当字符串长度很短(1或2)时可以直接解决。当字符串长度大于2时,为使较短回文子串至少可以向两侧遍历一次,循环中设置子串首位置需从1开始。因此,当字符串由连续多个相同字符组成时,如”ccc”,若不对下标为0的位置进行特殊讨论,会得到错误结果。特别地,若无法找到长度大于1的回文子串,返回第一个字符(字符串格式)即可。

2.3 具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class Solution {
public:
string longestPalindrome(string s) {
//字符串长度为1
if(s.length() == 1){
return s;
}else{
//字符串长度为2
if(s.length() == 2){
if(s[0] == s[1]){
return s;
}else{
return s.substr(0,1);
}
}else{
//字符串长度大于2
int begin = 0;
int end = 0;
int left = 0;
int right = 0;
int maxi = 1;
//对下标为0的位置特殊处理
for(int i = 1; ; i++){
if(i == s.length()){
return s;
}
if(s[i] != s[0]){
break;
}else{
maxi++;
}
}
for(int i = 1; i < s.length() - 1; i++){
//较短回文子串长度为1
if(s[i+1] != s[i]){
left = i;
right = i;
//若前后字符存在
while(left > 0 && right < s.length() - 1){
//前后字符相等,移动首尾位置
if(s[left-1] == s[right+1]){
left = left - 1;
right = right + 1;
}else{
break;//前后字符不等,退出循环
}
}
//所得回文子串长度较大,修改最大结果
if(right - left + 1 > maxi){
begin = left;
end = right;
maxi = right - left + 1;
}
}else{
//较短回文子串长度为多个连续相等字符
left = i;
right = i + 1;
while(right < s.length() - 1 && s[right+1] == s[right]){
right++;//字符相等,尾位置继续移动
}
//若前后字符存在
while(left > 0 && right < s.length() - 1){
//前后字符相等,移动首尾位置
if(s[left-1] == s[right+1]){
left--;
right++;
}else{
break;//前后字符不等,退出循环
}
}
//所得回文子串长度较大,修改最大结果
if(right - left + 1 > maxi){
begin = left;
end = right;
maxi = right - left + 1;
}
}
}
//若最长子串长度为1,返回首字符(字符串格式)
if(maxi == 1){
return s.substr(0,1);
}else{
return s.substr(begin, maxi);//返回最长子串
}
}
}
}
};

2.4 不足与收获

  • 代码分类讨论较多,经一定改进可以减少讨论,使代码更简洁。

  • 目前只有c++代码,日后有空可以补写python代码。

  • leetcode的输入输出与通常程序区别较大,本次进一步加深对leetcode输入输出的理解。

3 今后安排

  • 使用markdown写一份leetcode题解的工作量不低于写一道新题,时间原因不可能每道题都发布题解。争取每日一题,每周2-3份题解吧。

  • 最近开始学习b站古月居的ROS入门,根据学习情况随缘笔记吧。

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment