力扣-5-最长回文字串

很明显是动态规划
这类字符串的动规问题一般是定义一个一维dp数组,dp[i]表示:前i个字符中XXX的长度
这里是否适用呢?
假设dp[i]表示前i个字符(包括i)中最长回文字符串的长度,能找到状态转移方程吗?
感觉有点麻烦,关键是这个回文的判断与递增长度的关系

看一眼题解
这里的动态规划没有直接着眼于最长回文子串长度的状态转移
而是对于回文串判断的状态转移:如果字符[i,j]是一个回文串,那么字符[i+1,j-1]是回文串,且字符i==j
但是这个怎么…或许不是像最上面那样,而是去扫描每个字符,但后按照上面的状态转移方程判断最长(向两边拓宽),同时动态更新最长长度,或许还能做一点剪枝
试着写写吧

string longestPalindrome(string s) {

	// 如何保存最长的结果并返回呢?
	int maxLen = 1,startIndex;
	// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
	for (int i = 1; i < s.size() - 1; i++) {
		int j = i - 1, k = i + 1;
		while (j >= 0 && k <= s.size()) {
			if (s[j] == s[k]) {
				maxLen = max(maxLen, k - j + 1);
				startIndex = j;
				j--;
				k++;
			}
			else break;
		}
	}
	string str;
	for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
	return str;
}

这是我写出的第一版代码,但是有个致命问题,就是没考虑到aabb这种双数的情况
因为像aa这种情况只会出现在第一次初始化的情况

string longestPalindrome(string s) {

// 如何保存最长的结果并返回呢?
int maxLen = 1,startIndex;
// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
// 这么写,如果有存在长度一样的回文子串,后面的会覆盖前面的结果
// 这里没考虑到aabb这样双数的情况
for (int i = 1; i < s.size() - 1; i++) {
	// 因为上面的范围设定,所以出事的j,k一定是合法的
	int j = i - 1, k = i + 1;
	if (s[j] != s[k]) {
		if (s[j] == s[i]) k = i;
		else if (s[i] == s[k]) j = i;
	}
	while (j >= 0 && k <= s.size()) {
		if (s[j] == s[k]) {
			maxLen = max(maxLen, k - j + 1);
			startIndex = j;
			j--;
			k++;
		}else break;
	}
}
string str;
for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
return str;

}

这是第二版的代码,我以为我解决了aabb的问题,但是没考虑好初始化,比如a
`for (int i = 1; i < s.size() - 1; i++)`这里for循环的写法要求长度至少为3
那么就要额外考虑长度为1,和2的情况

```C++
string longestPalindrome(string s) {

	if (s.size() == 1) return s;
	else if (s.size() == 2) {
		if (s[0] == s[1]) return s;
		else {
			string s(1, s[0]);
			return s;
		}
	}

	// 如何保存最长的结果并返回呢?
	int maxLen = 1,startIndex;
	// 排除单字符,s[0]肯定不是个回文串,同理s[s.size()-1]也不会是
	// 这么写,如果有存在长度一样的回文子串,后面的会覆盖前面的结果
	// 这里没考虑到aabb这样双数的情况
	for (int i = 1; i < s.size() - 1; i++) {
		// 因为上面的范围设定,所以出事的j,k一定是合法的
		// 但要是这么写,长度至少为3
		int j = i - 1, k = i + 1;
		if (s[j] != s[k]) {
			if (s[j] == s[i]) k = i;
			else if (s[i] == s[k]) j = i;
		}
		while (j >= 0 && k <= s.size()) {
			if (s[j] == s[k]) {
				maxLen = max(maxLen, k - j + 1);
				startIndex = j;
				j--;
				k++;
			}else break;
		}
	}
	string str;
	for (int i = startIndex; i < startIndex + maxLen; i++) str += s[i];
	return str;
}

很不优雅地补了一段,还是不对,aaaa的情况不对

思路乱了,要考虑核长度1和核长度2向两边拓展的情况

中心拓展法

再瞄一眼题解
其实到目前已经不是动态规划了,而是中心拓展,另外就是这里考虑到中心长度有1和2两种情况,所以要单独抽出一个向两边拓展的函数

pair<int, int> expand(string s, int i, int j) {
	while(i>=0&&j<s.size()&&s[i]==s[j]){
		--i;
		++j;
	}
	return { i+1,j-1 };
}

string longestPalindrome(string s) {
	int start = 0, end = 0;
	for (int i = 0; i < s.size(); ++i) {
		pair<int,int> p1 = expand(s, i, i);
		pair<int, int> p2 = expand(s, i, i + 1);// 这里返回值成长度可能为负
		if (p1.second - p1.first > end - start) {
			start = p1.first;
			end = p1.second;
		}
		if (p2.second - p2.first > end - start) {
			start = p2.first;
			end = p2.second;
		}
	}
	return s.substr(start, end - start + 1);
}

动态规划

还是要用动态规划做一遍

马拉车算法

参考:

posted @ 2022-10-26 11:14  YaosGHC  阅读(20)  评论(0编辑  收藏  举报