题目:
解答:
方法一:暴力破解法
方法二:O(n^2)的算法
如果我们换一种思路,我们从里向外来判断。也就是我们先判断子字符串A是不是对称的。如果A不是对称的,那么向该子字符串两端各延长一个字符得到的字符串肯定不是对称的。如果A对称,那么我们只需要判断A两端延长的一个字符是不是相等的,如果相等,则延长后的字符串是对称的。因此在知道A是否对称之后,只需要O(1)的时间就能知道aAa是不是对称的。
具体代码如下所示。
1 // Get the longest length of its all symmetrical substrings 2 // Time needed is O(T^2) 3 int GetLongestSymmetricalLength_2(char* pString) 4 { 5 if(pString == NULL) 6 { 7 return 0; 8 } 9 10 int symmeticalLength = 1; 11 12 char* pChar = pString + 1; // notice here here here here 13 while(*pChar != '\0') 14 { 15 // Substrings with odd length:奇数长度 16 char* pFirst = pChar - 1; 17 char* pLast = pChar + 1; 18 while(pFirst >= pString && *pLast != '\0' && *pFirst == *pLast) 19 { 20 pFirst--; 21 pLast++; 22 } 23 24 int newLength = pLast - pFirst - 1; 25 if(newLength > symmeticalLength) 26 { 27 symmeticalLength = newLength; 28 } 29 30 // Substrings with even length:偶数长度 31 pFirst = pChar - 1; 32 pLast = pChar; 33 while(pFirst >= pString && *pLast != '\0' && *pFirst == *pLast) 34 { 35 pFirst--; 36 pLast++; 37 } 38 39 newLength = pLast - pFirst - 1; 40 if(newLength > symmeticalLength) 41 { 42 symmeticalLength = newLength; 43 } 44 pChar++; 45 } 46 47 return symmeticalLength; 48 }
由于子字符串的长度可能是奇数也可能是偶数。长度是奇数的字符串是从只有一个字符的中心向两端延长出来,而长度为偶数的字符串是从一个有两个字符的中心向两端延长出来。因此我们的代码要把这种情况都考虑进去。
在上述代码中,我们从字符串的每个字符串两端开始延长,如果当前的子字符串是对称的,再判断延长之后的字符串是不是对称的。由于总共有O(n)个字符,每个字符可能延长O(n)次,每次延长时只需要O(1)就能判断出是不是对称的,因此整个函数的时间效率是O(n2)。
方法三:中心扩展法
中心扩散法怎么去找回文串?
从每一个位置出发,向两边扩散即可。遇到不是回文的时候结束。举个例子,str = acdbbdaastr 我们需要寻找从第一个 b(位置为 3)出发最长回文串为多少。怎么寻找?
(1)首先往左寻找与当期位置相同的字符,直到遇到不相等为止。
(2)然后往右寻找与当期位置相同的字符,直到遇到不相等为止。
(3)最后左右双向扩散,直到左和右不相等。如下图所示:
每个位置向两边扩散都会出现一个窗口大小(len)。如果 len>maxLen(用来表示最长回文串的长度)。则更新 maxLen 的值。
因为我们最后要返回的是具体子串,而不是长度,因此,还需要记录一下 maxLen 时的起始位置(maxStart),即此时还要 maxStart=len。
1 class Solution { 2 public: 3 string longestPalindrome(string s) 4 { 5 int len=s.size(); 6 if(len==0||len==1) 7 { 8 return s; 9 } 10 int start=0;//记录回文子串起始位置 11 int end=0;//记录回文子串终止位置 12 int mlen=0;//记录最大回文子串的长度 13 for(int i=0; i < len; i++) 14 { 15 int len1=expendaroundcenter(s,i,i);//一个元素为中心 16 int len2=expendaroundcenter(s,i,i+1);//两个元素为中心 17 mlen=max(max(len1,len2),mlen); 18 if(mlen > end - start + 1) 19 { 20 start=i-(mlen-1)/2; 21 end=i+mlen/2; 22 } 23 } 24 return s.substr(start,mlen); 25 //该函数的意思是获取从start开始长度为mlen长度的字符串 26 } 27 private: 28 // 计算以left和right为中心的回文串长度 29 int expendaroundcenter(string s,int left,int right) 30 { 31 int L=left; 32 int R=right; 33 while(L>=0 && R < s.length() && s[R]==s[L]) 34 { 35 L--; 36 R++; 37 } 38 return R-L-1; 39 } 40 };