https://leetcode-cn.com/problems/longest-palindromic-substring/
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
四种算法:
一、暴力法
双指针双重循环切分字符串,双指针查找对比。
时间复杂度O(n^3)
char* longestPalindrome(char* s) {
int length = strlen(s),l =0,r = length-1,index1=0,index2=0,max = 0,n = 0,flag=1;
char *p=NULL;
if(length<=1) return s;
for(int i = 0;i<length-1;i++){ //i,j切分出所有可能字符串
for(int j = i+1;j<length;j++){
flag = 1,n=0;
for(int k = i,m = j;k<=m;k++,m--){ //验证该字符串是否是回文
if(s[k]!=s[m]){ //如果有不同,则不是回文
flag = 0;
break;
}
if(m!=k) n+=2;
else n++;
}
if(flag){ //若是回文,且长度大于已知最长,记录下标
if(n>max){
max = n;
index1 = i;
index2 = j;
}
}
}
}
p = (char*)malloc(sizeof(char)*(index2-index1+2));//复制回文串
for(int i = index1;i<= index2;i++){
p[i-index1] = s[i];
}
p[index2-index1+1] ='\0';
return p;
}//时间复杂度O(n^3)空间复杂度O(1)
二、中心拓展算法
n个字符串有2*n-1个回文中心,比如abbab,对于单个字符,中心是它本身,如a,对于双字符,中心是空,如bb。
只要从中心向两端匹配,就能匹配出当前回文串的长度。
int expand(char *s,int left,int right,int length);
char* longestPalindrome(char* s) {
int length=strlen(s),l=0,r=0;
char *p=NULL;
for(int i = 0;i < length;i++){ //最基本的回文单位是单字符或者相同双字符。
int len1 = expand(s,i,i,length); //遍历所有单字符
int len2 = expand(s,i,i+1,length); //遍历所有连续双字符
int len = len1 > len2 ? len1 : len2;
if(len > r - l){ //这句话等于(len>=r-l+1)而r-l+1为同样长的字符串
l = i - (len-1)/2; //根据中点值,计算左右界限
r = i + len/2;
}
}
p = (char *)malloc(r-l+2); //复制数组
for(int i = l;i<=r;i++){
p[i-l]=s[i];
}
p[r-l+1] = '\0';
return p;
}
int expand(char *s,int left,int right,int length){ //拓展函数,返回长度
int l = left,r = right;
while(l>=0 && r<length && s[l]==s[r]){
l--;
r++;
}
return r-l-1; //当前值已经不满足,实际上满足的下标是 l+1和r-1,最终长度为r-l-1
}时间复杂度O(n^2) 空间复杂度O(1)
三、动态规划
设置辅助数组dp[length][length],以若从起始点为i,结束点为j,且i,j范围内的字符串是回文,那么dp[i][j]=1。
边界条件,dp[i][i] = 1,dp[i][i+1] = dp[i] == dp[i+1].
状态转移方程:dp[i][j] = dp[i+1][j-1] && dp[i]==dp[j];
char* longestPalindrome(char* s) {
int length = strlen(s),max = 0,index1=0,index2=0;
char **p=NULL,*p1=NULL;
p = (char **) malloc(sizeof(char*)*length);
for(int i = 0;i<length;i++){
p[i] = (char*)malloc(sizeof(char)*length);
}
for(int i = 0;i<length;i++){ //dp前先做好基本准备
p[i][i]=1;
if(i<length-1){
if(s[i]==s[i+1]){
p[i][i+1]=1;
max = 2;
index1 = i;
index2 = i+1;
}
}
}
for(int i = 3;i<=length;i++){ //根据长度dp,这样无需与最大值比较
for(int j=0;i+j-1<length;j++){ //j为起始点,k为终点
int k = i+j-1;
if(s[j]==s[k] && p[j+1][k-1]==1){ //状态转移方程
p[j][k] = 1;
max = i;
index1 = j;
index2 = k;
}
}
}
p1 = (char*)malloc(index2-index1+2); //复制数组
for(int i = index1;i<=index2;i++){
p1[i-index1] = s[i];
}
p1[index2-index1+1] = '\0';
return p1;
}时间复杂度O(n^2) 空间复杂度O(n^2)
四、Manacher算法
请自行阅读相关文献
char* longestPalindrome(char* s) {
int length = strlen(s),*len=NULL;
int mid=1,mx=0,max_mid=0,max = 0;
char *p=NULL,*p2=NULL;
if(length==0) return s;
p = (char *)malloc(length+length+3);
p[0] = '$';
p[1] = '#';
for(int i=0,j=2;i<length;i++){
p[j++] = s[i];
p[j++] = '#';
if(i==length-1) p[j] = '\0';
} //构建新字符串
length = strlen(p); //重置长度
len = (int *)malloc(sizeof(int)*(length));//辅助数组
for(int i=0;i<length;i++) len[i] = 0;//可以不初始化
for(int i = 1;i<length;i++){
if(i<mx) len[i] = len[2*mid-i] < mx-i? len[2*mid-i]:mx-i; //如果当前值在最右极点的右左边
else len[i]=1; //有两种情况,一是左对称点的长度在左极点内,这样可以确定对称点长度相同
while(p[i+len[i]]==p[i-len[i]]){//另一种情况是对称点长度越出左极点,这样确定极点长度内的
len[i]++; //元素一定是回文串,接下来不断对比左右,拓展已知长度、
}
if(i+len[i]>mx){ //若新的右界大于当前右界,则重置中点,重置右界
mx = i+len[i];
mid = i;
}
if(len[i]>max){ //记录最长串中点和半径
max = len[i];
max_mid = i;
}
}
p2 = (char *)malloc(max+2); //将回文串复制到新的数组。
for(int i = max_mid - max +2,j=0;i<=max_mid+max-2;i++){
if(p[i]=='#') continue;
else{
p2[j++] = p[i];
}
if(i==max_mid+max-2) p2[j]='\0';
}
return p2; //返回
}//时间复杂度O(n) 空间复杂度O(n)