问题
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
思路
验证回文串的方法就是两个两个的对称验证是否相等。查找回文字串的方法,就要以每一个字符为中心,像两边扩散来寻找回文串,这个算法的时间复杂度是O(n*n),由于回文串的长度可奇可偶,需要注意下奇偶情况,比如”bob”是奇数形式的回文,”noon”就是偶数形式的回文。
class Solution {
public:
void findPalindrome(string s, int left, int right, int &startIndex, int &maxLen){
int step = 1;
while((left - step) >= 0 && (right + step) <= s.length())
{
if(s[left - step] != s[right + step])
{
break;
}
++step;
}
int wide = right - left + 2 * step - 1; //step初始化为1,包含中心点,减去1
if(maxLen < wide) //当前长度大于之前的len,即更新
{
maxLen = wide;
startIndex = left - step + 1; //left包含在step中,加1
}
}
string longestPalindrome(string s) {
int len = s.length();
int left = 0;
int right = 0;
int startIndex = 0;
int maxLen = 0;
for(int i = 0; i < len; ++i)
{
if(s[i] == s[i + 1])
{
left = i;
right = i + 1;
findPalindrome(s, left, right, startIndex, maxLen);
}
left = i;
right = i; //出现相同字符的情况,需要再判断奇回文的情况,例如ccc
findPalindrome(s, left, right, startIndex, maxLen);
}
return s.substr(startIndex, maxLen);
}
};
动态规划的解法,维护一个dp[i][j]的二维数组,其元素值表示的是从i到j,是否为回文串。值得注意的地方是:当i,j不相邻的情况,需另外确认dp[i+1][j-1]是否为回文串。
下面是一开始写的代码。没有考虑清楚i,j的遍历范围。动态数组的值,必须是根据前面已知值,推后续值。条件分类也可以再简化。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
int left = 0;
int right = 0;
int maxLen = 0;
int dp[len][len] = {0};
for(int i = 0; i < len; ++i)
{
for(int j = 0; j < len; ++j)
{
if(j == i)
{
dp[i][j] = 1;
}
else if(j == i + 1)
{
if(s[i] == s[j])
{
dp[i][j] = 1;
}
}
else if(j > i + 1) //dp[i + 1][j - 1]还未计算
{
if(s[i] == s[j] && dp[i + 1][j - 1] == 1)
{
dp[i][j] = 1;
}
}
}
}
return s.substr(startIndex, maxLen);
}
};
特殊之处在于,一维在j,二维在i。这样保证了计算当前dp值时,前面值都已计算。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
int left = 0;
int right = 0;
int maxLen = 0;
int dp[len][len] = {0};
for(int i = 0; i < len; ++i)
{
for(int j = 0; j < i; ++j)
{
if((s[j] == s[i]) && (i - j < 2 || dp[j + 1][i - 1] == 1))
{
dp[j][i] = 1;
}
else
{
dp[j][i] = 0;
}
if(dp[j][i] == 1 && maxLen < i - j + 1)
{
maxLen = i - j + 1;
left = j;
right = i;
}
}
dp[i][i] = 1;
}
return s.substr(left, right - left + 1); //substr的第二个参数,不能直接使用maxLen,因为单个元素的字符串会返回为空
}
};
马拉车算法,可达到O(n)的时间复杂度。目前在刷动态规划,优化什么的,之后再说吧。
C++小知识
||只要满足第一个条件,后面的条件就不再判断
通过传引用的方式改变局部变量,代替全局变量的使用。
二维数组和二维向量的创建与初始化:int dp[s.size()][s.size()] = {0}
一维数组的动态分配,初始化和撤销:
动态分配: int *array=new int [n];
初始化: memset(array,0,n*sizeof(array));
撤销: delete [] array;
二维数组的动态分配,先动态分配了一个10单元的数组的指针的指针的首地址给*array,然后再对其每个首地址进行遍历,完成一个5单元的数组的动态分分配,并把首地址给array[i]
int **array;
array=new int *[10];
for(int i=0;i<10;i++)
array[i]=new int [5];
二维数组的动态初始化,memset只作用于一块连续的内存空间
int **array;
array=new int *[10];
for(int i=0;i<10;i++)
{
array[i]=new int [5];
memset(array[i],0,5*sizeof(int)); //注意这里是array[i]
}
二维数组的撤销,先里层再外层
for (int i = 0; i < 10; i ++)
{
delete[] array[i];
}
delete [] array;