1、最长回文子串
题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
- 1 <= s.length <= 1000
- s 仅由数字和英文字母(大写和/或小写)组成
1.0、两边向中间缩进(暴力)
这个其实就是暴力解法的优化一点点的玩意儿。
string longestPalindrome0(string s)
{
int size = s.size();
string result = "";
int leftIndex, rightIndex;
for (int i = 0; i < size; i++) //size-i为需要验证子串的长度
{
for (int j = 0; j <= i; j++) //i+1为需要验证的子串的个数,j为开始验证的下标
{
leftIndex = j;
rightIndex = j + size - i - 1;
bool flag = true;
while (leftIndex < rightIndex && flag == true)
{
if (s[leftIndex] != s[rightIndex])
flag = false;
leftIndex++;
rightIndex--;
}
if (flag == true)
return s.substr(j, size - i);
}
}
return result;
}
时间复杂度:O(n三方)
空间复杂度:O(1)
1.1、动态规划
(这玩意儿实在是不好写,就不写了_)
状态转移方程:
边界条件:
string longestPalindrome1(string s)
{
int size = s.size();
if (size < 2)return s;
string result;
int maxLength = 1;
int beginIndex = 0;
vector<vector<bool>> dp(size, vector<bool>(size, false)); //创建size*size大小的状态保存数组,dp[i][j]表示i到j的字符串是否为回文
for (int i = 0; i < size; i++) //单个字符肯定为回文,标识为true
dp[i][i] = true;
for (int length = 2; length <= size; length++) //length为子字符串长度,从2开始,是因为1就是1啊。。。
{
for (int leftIndex = 0; leftIndex < size; leftIndex++) //子字符串的左下标
{
int rightIndex = leftIndex + length - 1;
if (rightIndex >= size)break;
if (s[leftIndex] == s[rightIndex])
{
if (rightIndex - leftIndex < 3) //对应边界条件,s[i]==s[j],i+1==j时满足回文长度为2,i+2==j时满足回文长度为3
dp[leftIndex][rightIndex] = true;
else
dp[leftIndex][rightIndex] = dp[leftIndex + 1][rightIndex - 1];
//保证上式成立的关键是,length由小及大,假设现在length为5,那么dp[leftIndex + 1][rightIndex - 1]在lengrh为3中就已确定
}
if (dp[leftIndex][rightIndex] && length > maxLength)
{
maxLength = length;
beginIndex = leftIndex;
}
}
}
return s.substr(beginIndex, maxLength);
}
时间复杂度:O(n平方)
空间复杂度:O(n平方)
和暴力解法对比一下,其实就是用一个n平方空间把暴力解法中的那个while换掉了
1.2、中心扩展法
如果字符串是奇数(bab型),可以以中间某个字符为起点,向两边开始检查是否为回文,但如果是偶数(baab型)这样做就是错的
。下面是两种解决方法
将连续的字符看作是一个整体
string longestPalindrome2(string s)
{
string result;
int size = s.size();
int leftIndex, rightIndex;
int maxLength = 0;
for (int i = 0; i < size; i++)
{
int tempLength = 1;
leftIndex = i - 1;
rightIndex = i + 1;
while (i + 1 < size && s[i + 1] == s[i]) //将连续的字符归为一个整体
{
i++;
rightIndex = i + 1;
tempLength++;
}
while (leftIndex >= 0 && rightIndex < size) //开始向两边检测
{
if (s[leftIndex] != s[rightIndex])
break;
leftIndex--;
rightIndex++;
tempLength += 2;
}
if (tempLength > maxLength)
{
maxLength = tempLength;
result = s.substr(leftIndex + 1, tempLength);
}
}
return result;
}
时间复杂度:O(n平方)
空间复杂度:O(1)
一般都用这个,简单,还快,还不占内存
在字符串中间插入 # ,bab就变成了#b#a#b#,baab就变成了#b#a#a#b#,看一看是不是都是奇数了
string longestPalindrome2(string s)
{
string sourceString;
for (auto& i : s)
{
sourceString.push_back('#');
sourceString.push_back(i);
}
sourceString.push_back('#');
string result;
string tempresult;
int size = sourceString.size();
int leftIndex, rightIndex;
int maxLength = 0;
for (int i = 0; i < size; i++)
{
int tempLength = 1;
leftIndex = i - 1;
rightIndex = i + 1;
while (leftIndex >= 0 && rightIndex < size)
{
if (sourceString[leftIndex] != sourceString[rightIndex])
break;
leftIndex--;
rightIndex++;
tempLength += 2;
}
if (tempLength > maxLength)
{
maxLength = tempLength;
tempresult = sourceString.substr(leftIndex + 1, tempLength);
}
}
for (auto& i : tempresult)
if (i != '#')
result.push_back(i);
return result;
}
时间复杂度:O(n平方)
空间复杂度:O(n)
不用考虑奇,偶,占用空间可以接受,也比较常用
1.3、其他
Manacher 算法,时间复杂度:O(n),空间复杂度:O(n),有点难,我也不会,想学习的可以去百度(-_-!)。