Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
初看到这道题,没什么特别好的想法,就直接百度了一下题解,并尝试了两种解法。
方法一:动态规划。时间复杂度o(n2),空间复杂度o(n2)
P(i,j)为1时代表字符串Si到Sj是一个回文,为0时代表字符串Si到Sj不是一个回文。我们有:
(1) P(i,j)= P(i+1,j-1)(如果S[i] = S[j])。这是动态规划的状态转移方程。
(2) P(i,i)= 1,P(i,i+1)= 1 (如果S[i]= S[i+1])。这是初始条件,分别对应子串长度是奇数或者偶数的情况。
C语言代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* longestPalindrome(char *);
int main(){
char s[]="ibvjkmpyzsifuxcabqqpahjdeuzaybqsrsmbfplxycsafogotliyvhxjtkrbzqxlyfwujzhkdafhebvsdhkkdbhlhmaoxmbkqiwiusngkbdhlvxdyvnjrzvxmukvdfobzlmvnbnilnsyrgoygfdzjlymhprcpxsnxpcafctikxxybcusgjwmfklkffehbvlhvxfiddznwumxosomfbgxoruoqrhezgsgidgcfzbtdftjxeahriirqgxbhicoxavquhbkaomrroghdnfkknyigsluqebaqrtcwgmlnvmxoagisdmsokeznjsnwpxygjjptvyjjkbmkxvlivinmpnpxgmmorkasebngirckqcawgevljplkkgextudqaodwqmfljljhrujoerycoojwwgtklypicgkyaboqjfivbeqdlonxeidgxsyzugkntoevwfuxovazcyayvwbcqswzhytlmtmrtwpikgacnpkbwgfmpavzyjoxughwhvlsxsgttbcyrlkaarngeoaldsdtjncivhcfsaohmdhgbwkuemcembmlwbwquxfaiukoqvzmgoeppieztdacvwngbkcxknbytvztodbfnjhbtwpjlzuajnlzfmmujhcggpdcwdquutdiubgcvnxvgspmfumeqrofewynizvynavjzkbpkuxxvkjujectdyfwygnfsukvzflcuxxzvxzravzznpxttduajhbsyiywpqunnarabcroljwcbdydagachbobkcvudkoddldaucwruobfylfhyvjuynjrosxczgjwudpxaqwnboxgxybnngxxhibesiaxkicinikzzmonftqkcudlzfzutplbycejmkpxcygsafzkgudy";
printf("%d\n",strlen(s));
printf("%s\n",longestPalindrome(s));
return 0;
}
char* longestPalindrome(char* s) {
int n = strlen(s);
int longestBegin = 0;
int maxLen = 1;
int table[1001][1001];
memset(table,0,1001*1001);
int i,j,len;
for (i = 0; i < n; i++) {
table[i][i] = 1;
}
for (i = 0; i < n-1; i++) {
if (s[i] == s[i+1]) {
table[i][i+1] = 1;
longestBegin = i;
maxLen = 2;
}
}
for (len = 3; len <= n; len++) {
for (i = 0; i < n - len + 1; i++) {
j = i + len - 1;
if (s[i] == s[j] && table[i+1][j-1]) {
table[i][j] = 1;
longestBegin = i;
maxLen = len;
}
}
}
strncpy(s,s+longestBegin,maxLen);
s[maxLen] = '\0';
return s;
}
不过在提交的时候出现了问题。对于某个字符串长度为877的测试案例,我的机器上能输出正确的结果“fklkf”,但在leetcode上却输出了错误的结果。这个问题我百思不得其解。因此在这里把main函数的代码也贴出来,希望哪个朋友发现问题了,麻烦告知我一声,先谢谢了。
方法二:中心展开法。时间复杂度o(n2),空间复杂度o(1)
这个算法思想其实很简单啊,时间复杂度为O(N2),空间复杂度仅为O(1)。就是对给定的字符串S,分别以该字符串S中的每一个字符C为中心,向两边扩展,记录下以字符C为中心的回文子串的长度。但是有一点需要注意的是,回文的情况可能是 a b a,也可能是 a b b a。就像动态规划解法需要设置对应子串长度为奇数和偶数两种情况的不同初始条件,这里我们需要对子串长度为奇数或者偶数时分别考虑。即如果有S[i]==S[i+1],则不仅需要考虑子串长度为奇数的情况(通常情况,每次从中心字符向两边展开时都需要考虑这种情况),还需要考虑子串长度为偶数的情况。
C语言代码如下:
void ifPalindrome(char *s, int i , int len, int *subBegin, int *subLen){
int begin1 = -1, len1 = 0;
int begin2, len2, j;
if(i >= 0 && i + 1 < len && s[i] == s[i+1]){
begin1 = i;
len1 = 2;
j = 1;
while(i - j >= 0 && i + 1 + j < len && s[i - j] == s[i + 1 + j]){
begin1 = i - j;
len1 = len1 + 2;
j++;
}
}
begin2 = i;
len2 = 1;
j = 1;
while(i - j >= 0 && i + j < len && s[i - j] == s[i + j]){
begin2 = i - j;
len2 = len2 + 2;
j++;
}
if(len1 > len2){
*subBegin = len1 > *subLen ? begin1 : *subBegin;
*subLen = len1 > *subLen ? len1 : *subLen;
}
else{
*subBegin = len2 > *subLen ? begin2 : *subBegin;
*subLen = len2 > *subLen ? len2 : *subLen;
}
return;
}
char* longestPalindrome(char* s) {
int n = strlen(s);
int i,subBegin = -1, subLen = 0;
for(i = 0; i < n; i++){
ifPalindrome(s, i, n, &subBegin, &subLen);
}
strncpy(s, s+subBegin, subLen);
s[subLen] = '\0';
return s;
}
方法三:传说中的Manacher算法。时间复杂度O(N)
这次没怎么研究,留待以后吧。
小结:
(1)动态规划的本质:用空间换时间。将重叠子问题的解保存下来,减少重复计算。
(2)判断一个子串是否回文时,通常需要考虑子串长度分别为奇数和偶数的情况。