5.最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: “babad”
输出: “bab”
注意: "aba"也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
这个算法中如果使用暴力法去求解,很明显需要选择所有子字符串的位置,并检查它是不是回文,子字符串有n(n-1)/2的情况,验证每个字符串需要O(n)的时间,故运行时间复杂度为O(n3)。那么如何提高呢?可以使用动态规划的思路,考虑“ababa”如果已知了“bab”是回文,且他两端的字母相同,那么这个字符串也一定是回文:
这是一个很直观的动态规划解法,首先初始化一字母和二字母的回文,然后找出所有的三字母回文。但是这样的算法的空间复杂度为O(n2),时间复杂度也为O(n2)。算法如下:
class Solution {
public:
string longestPalindrome(string s) {
int n = s.length();
vector<vector<bool>> table(s.length(), vector<bool>(s.length(), false));
int longestbegin = 0;
int maxlen = 1;
for(int i = 0;i < n;i++){
table[i][i] = true;
if(i != n && s[i] == s[i+1]){
table[i][i+1] = true;
longestbegin = i;
maxlen = 2;
}
}
for(int len = 3;len <= n; len++){
for(int i = 0;i < n-len+1;i++){
int j = i + len -1;
if(s[i] == s[j] && table[i+1][j-1]){
table[i][j] = true;
longestbegin = i;
maxlen = len;
}
}
}
return s.substr(longestbegin, maxlen);
}
};
第二种思路是使用中心拓展法,主要就是把给定字符串的每个字母当做中心,向两边查找最大的回文子串,注意奇数和偶数的情况。其空间复杂度为O(1),时间复杂度为O(n2)。
class Solution {
private:
int left = 0, maxlen = 1;
public:
void findMaxPalindrome(string s, int i, int j){
while(i >= 0 && j < s.length() && s[i] == s[j]){
i--; j++;
}
if (maxlen < j-i-1){
left = i+1;
maxlen = j-i-1; //获得最大长度
}
}
string longestPalindrome(string s) {
if((s == "") || s.length() < 2)
return s; //极端情况
for(int i = 0;i < s.length()-1;i++){
// 两种情况:中间是bab,中间bb
findMaxPalindrome(s, i, i);
findMaxPalindrome(s, i, i+1);
}
return s.substr(left, maxlen);
}
};
6.Z字形变换
将字符串 “PAYPALISHIRING” 以Z字形排列成给定的行数:
P A H N
A P L S I I G
Y I R
之后从左往右,逐行读取字符:“PAHNAPLSIIGYIR”
实现一个将字符串进行指定行数变换的函数:
string convert(string s, int numRows); 示例 1:
输入: s = “PAYPALISHIRING”, numRows = 3
输出: “PAHNAPLSIIGYIR” 示例 2:输入: s = “PAYPALISHIRING”, numRows = 4
输出: “PINALSIGYAHRPI”
其实Z字形就相当于波浪线这样来走的,所以可以根据这个规律去思考算法的结构,总的来说就是从第零行开始向下走,到了第n行然后向上走,最后按照每行的元素进行重新排列。
class Solution {
public:
string convert(string s, int nRows) {
int step = 1, row = 0; //1是向下,-1是向上
vector<string> res(nRows, "");
string result = "";
if(nRows < 2) return s;
for(int i = 0;i < s.length();i++){
res[row] += s[i]; //strIng类型的优势
if(row == nRows-1)
step = -1;
else if(row == 0)
step = 1;
row = row + step;
}
for(int i = 0;i < nRows;i++){
result += res[i];
}
return result;
}
};
7. 翻转整数
给定一个 32 位有符号整数,将整数中的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。
class Solution {
public:
int reverse(int x)
{
long long res = 0;
while(x!=0)
{
res = res*10+x%10;
x/=10;
if(res >= INT_MAX || res <= INT_MIN)
return 0;
}
return res;
}
};
8. 字符串转整数 (atoi)
实现 atoi,将字符串转为整数。
该函数首先根据需要丢弃任意多的空格字符,直到找到第一个非空格字符为止。如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值。如果第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
字符串可以在形成整数的字符后面包括多余的字符,这些字符可以被忽略,它们对于函数没有影响。
当字符串中的第一个非空字符序列不是个有效的整数;或字符串为空;或字符串仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回 0。
说明:
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。如果数值超过可表示的范围,则返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
class Solution {
public:
int atoi(const char *str) {
int k = 0;
long ans = 0L; //建议设为long型,为了判断溢出
bool sig = false; //默认正数
string s(str);
for (int i = 0; i < s.length(); i++) {
if (s[i] != ' ') {
k = i;
break;
}
}
if (s[k] == '-' || s[k] == '+') {
sig = s[k] == '-' ? true : false;
k++;
}
while (s[k] >= '0' && s[k] <= '9') {
ans = ans * 10 + (s[k] - '0');
k++;
if ( (sig ? -1 : 1) * ans >= INT_MAX) return INT_MAX; //在这里进行溢出判断
if ( (sig ? -1 : 1) * ans <= INT_MIN) return INT_MIN;
}
ans = (sig ? -1 : 1) * ans;
return ans;
}
};
9. 回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
class Solution {
public:
bool isPalindrome(int x) {
int reverse = 0;
if ((x < 0 || x%10 == 0) && (x != 0)) return false; //去除特例
while(x > reverse){ //当商小于reverse时候,说明到一半了
reverse = reverse*10 + x % 10;
x = x / 10;
}
return (reverse/10 == x || reverse == x); //当奇个数时,reverse的最后一位为中间数
}
};
声明
以上代码均参考于牛客网并在牛客网测试成功。参考了各位大佬的思路整理而成,希望大家一起学习~