算法:最长回文长度

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010368556/article/details/89187285

问题描述

     给定一个字符串,求它的最长回文子串的长度(连续长度或者不连续长度)。

     回文串就是正着读和反着读都一样的字符串。

问题一:判断是否是回文

分析:根据定义进行判断,首尾不一致,则就不是回文

bool isPalinDrome(string& str, int begin, int end){
// 判断是否是回文
	int bg = begin, ed = end;

	while (bg <= ed){
		if (str[bg] == str[ed]){
			bg++;
			ed--;
		}
		else{
			return false;
		}
	}
	return true;
}

问题二:连续条件下最长回文长度

分析:给定一个字符串,求它的最长连续的回文子串的长度,这里介绍两种方法;

最长回文串(可不连续)的意思是以某个字符为轴,分别往左右遍历的公共子串的最大长度(可不连续),那么不如将最大回文串改为一个字符串的顺序与逆序的最大公共子串(可不连续)。

方法1:暴力法(BruteForce,BF)

先求出该字符串的每一个子串,然后再分别判断每个子串是否是回文串,找到长度最长的那个。其中求出每个子串的时间复杂度为O(n2),判断是否为回文串的复杂度为O(n),两者是相乘关系,所以整个算法的时间复杂度为O(n3)。

int lengthBF(string& str){
// 用暴力法进行求解
	int len = str.size();
	if (len == 0) return 0;
	if (len == 1) return 1;

	int longest = 1;
	for (int i = 0; i < len; i++){
		for (int j = i + 1; j < len; j++){
			if (isPalinDrome(str, i, j)){
				longest = longest>j - i + 1 ? longest : j - i + 1;
			}
		}
	}

	return longest;
}

方法2:动态规划

对于字符串str,假设dp[i,j]=1表示子串str[i...j]是回文子串,那个必定存在dp[i+1,j-1]=1。这样最长回文子串就能分解成一系列子问题,可以利用动态规划求解了。动态规划的时间复杂度也为O(n2)。

      首先构造状态转移方程

      上面的状态转移方程表示,当str[i]=str[j]时,如果str[i+1...j-1]是回文串,则str[i...j]也是回文串;如果str[i+1...j-1]不是回文串,则str[i...j]不是回文串。

      初始状态

  • dp[i][i]=1
  • dp[i][i+1]=1 if str[i]==str[i+1]

      上式表示的意义是单个字符,两个相同字符都是回文串。

int lengthDP(string& str){
// 用动态规划法进行求解
	int len = str.size();
	if (len == 0) return 0;
	if (len == 1) return 1;
	int longest = 1;
	vector<vector<int>> dp(len, vector<int>(len, 0));\
	for (int t = 0; t < len; t++){
		dp[t][t] = 1;
		if (str[t] == str[t + 1])
			dp[t][t + 1] = 1;
	}

	for (int i = 0; i < len; i++){
		for (int j = i + 1; j < len; j++){
			if (str[i] == str[j])
				dp[i][j] = dp[i + 1][j - 1];
			else
				dp[i][j] = 0;
			if (dp[i][j] == 1)
				longest = longest>j - i + 1 ? longest : j - i + 1;
		}
	}

	return longest;
}

问题三:不连续条件下字长回文长度

分析:定义dp_{i,j} 表示区间[i,j] 中最长回文串的长度(回文串的端点不一定是区间的端点),那么状态转移方程如下:

int LongestPalinDrome(string& str){
	// dp[i][j] 表示区间[i,j]之中的最长回文长度
	int len = str.size();
	if (len == 0) return 0;
	if (len == 1) return 1;
	int longest = 1;
	vector<vector<int>> dp(len, vector<int>(len, 0));
	
	for (int t = 0; t < len-1; t++){
		dp[t][t] = 1;
	}

	int tmp1 = 0, tmp2 = 0;
	for (int lgt = 2; lgt <= len; lgt++){			// lgt表示子区间长度
		for (int j = 0; j + lgt - 1 < len; j++){    // j表示子区间首序号
			if (str[j] == str[j + lgt - 1]){
				dp[j][j + lgt - 1] = dp[j + 1][j + lgt - 1 - 1] + 2;// dp[i][j]
			}
			tmp2 = std::max(dp[j + 1][j + lgt - 1], dp[j][j + lgt - 1 - 1]);
			dp[j][j + lgt - 1] = max(dp[j][j + lgt - 1], tmp2);
		}
	}

	return dp[0][len - 1];
}

参考博文:

https://blog.csdn.net/FlushHip/article/details/83830694

https://www.cnblogs.com/xiuyangleiasp/p/5070991.html

猜你喜欢

转载自blog.csdn.net/u010368556/article/details/89187285
今日推荐