思路一
依次穷举所有子串。
比如对于字符串 abcdefg
依次穷举
第一趟
a ab abc abcd abcde abcdef abcdefd
第二趟
b bc bcd bcde bcdef bcdefg
依此类推
判断一个字符串是否为回文:
int main() {
int flag = 1;
string s = "a";//测试字符串
if (s.length() == 0) {
printf("字符串为空");
return -1;
}
for (int m = 0; m < s.length()/2; m++) {
if (s[m] != s[s.length() - m - 1])
flag = 0;
}
if (flag == 1)
printf("yes");
else
printf("no");
return 0;
}
寻找最长回文子串完整代码如下
其实有些遍历可以提前终止。只要[length-1]或者[length-2]与当前遍历的字符所连成的字符串是回文串便可以直接返回了,因为它一定是最长的。但是由于这个方法中的串是从头向尾延伸的,所以差别不大。如果串从两端向中心延伸,可以节省一些时间。
#include
#include
using namespace std;
class Solution {
public:
string longestPalindrome(string s) {
int leng = s.length();
int flag = 1;
string res;//存储每次检查的子串
string res1 = "";
res1 = res1 + s[0];//存储最终结果
for (int i = 0; i < leng; i++) {
for (int j = i; j < leng; j++) {
flag = 1;
res = s.substr(i, (j - i + 1));//提取子串
for (int m = 0; m < res.length() / 2; m++) {
if (res[m] != res[res.length() - m - 1])
flag = 0;
}//判断是否回文
if (flag == 1) {
if ((res.length()) > res1.length()) {
res1 = res;
}
}
}
}
return res1;
}
};
int main() {
string s = "ac";//测试字符串
string res;
Solution cl;
res=cl.longestPalindrome(s);
cout << res << endl;
return 0;
}
这个方法效率贼低。提交的时候超时了。时间复杂度达到了O(n^3)
(要上课了,等会下课再搞,我先溜了)
来了来了
思路2(胡思乱想的垃圾,跳过即可)
串从两端向中间延伸。
其实这个思路是我最先想出来的,但是实现的时候没有成功,现在上完了课,来看一下是不是行得通。
先来分析一波。
首先思考回文子串的特征。两个及以上长度的回文子串有一个共同的特征:首尾字符必定是相同的。我们以此为出发点,思考以下的步骤。
从头扫描字符串进行回文子串的判断,在这个过程中不断更新回文子串,直至得出最优解。具体操作如下:
对于字符串asgafa
首先扫描字符a
,记录此时的扫描下表,为0,然后,从尾部开始寻找a
,当找到a
的时候,记下此时的位置,对于此字符串,得出此时位置为下标5,对0-5子串进行回文子串检测,如果是回文子串,则直接返回即可。如果不是,继续从后向前寻找a,直至收尾相碰。这样的话,也是三层for循环,差别不大,实际上还是遍历。如果想要提高效率,需要寻找更好的检测条件,减除那些不必要的遍历,此方法在停止遍历的条件有点混乱,仅能去除很少的一部分特殊字符串的不必要遍历,我试一试吧,看能不能AC。
之后,如果没有得到回文子串,直接返回首字符即可,即把长度为1的回文子串进行单独处理,上述过程寻找的是长度大于等于2的回文子串。
代码如下
#include
#include
using namespace std;
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
string temps;
string res;
int suc = 1;
int flag;
for(int i=0;i<len-1;i++){//头部走到[len-2]就可以停下了
for (int j = len - 1; j > i; j--) {
flag = 1;
if (s[i] == s[j]) {
temps = s.substr(i, j - i + 1);
for (int m = 0; m < temps.length() / 2; m++) {
if (temps[m] != temps[temps.length() - m - 1])
flag = 0;
}
if (flag == 0)
continue;//继续向前寻找
else {
if ((j == (len - 1) || j == (len - 2))&&suc<(j-i+1)) {//找到
return temps;
}
else {
if (suc < (j - i + 1)) {
res = temps;
suc = j - i + 1;
}
}
}
}
}
}
if (suc == 1) {
res = res + s[0];
}
return res;
}
};
int main() {
string s = "p";//测试字符串
string res;
Solution cl;
res = cl.longestPalindrome(s);
cout << res << endl;
return 0;
}
比思路1快了一些。但还是超出了时间限制。这个方法仅仅对一些特殊的字符串速度快,行不通。
菜狗只好去看题解了。
题解有几种方法,包括但不限于中心扩散法、动态规划方法、Manacher算法。以下分别进行分析。
(得,一下午加一晚上就弄一道题,我可真棒)
题解链接:
https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/
动态规划代码:
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
int head = 0;
int suc = 1;
int len2;
vector<vector<int>> table(len, vector<int>(len));
for (int i = 0; i < len - 1; i++) {
table[i][i] = 1;
}
for (int j = 0; j < len; j++) {
for (int i = 0; i < j; i++) {
if ((j - i) == 1) {
if (s[i] == s[j]) {
table[i][j] = 1;
}
else
table[i][j] = 0;
}
else {
table[i][j] = (!(s[i] ^ s[j])) && (table[i + 1][j - 1]);
}
if (table[i][j] == 1) {
len2 = j - i + 1;
if (suc < len2) {
suc = len2;
head = i;
}
}
}
}
return s.substr(head, suc);
}
};