给一个字符串,找出它的最长的回文子序列的长度。和腾讯的2016实习题目类似。
输入:goolgle
输出:4
goog是它的最长回文子序列
所说的子序列不一定是连续的,但是顺序不变。
子串必须是连续的字符组成。
假设S[i…j]是给定的字符串,长度为n,让dp[i][j]表示从s[i]到s[j]包含的最长回文子序列的长度。
初始化:dp[i][i] = 1;
如果s[i] =s[j],dp[i][j] = dp[i+1][j-1] +2;
如果s[i] != s[j],dp[i][j] = max(dp[i][j-1],dp[i+1][j])
可以得到它的递归程序
int lps(char *s ,int i,int j)
{
if(i == j)
return 1;
if(i>j)
return 0;
if(s[i] == s[j])
return lps(s,i+1,j-1)+2;
else
return max(lps(s,i,j-1),lps(s,i+1,j));
}
以一个长度为6的字符串为例:
L(1,4)出现重复计算,符合动态规划的性质。
采用自底向上的方法,将子序列的长度作为排序的依据,按从小到大进行求解。
#include
#include
#include
using namespace std;
int Max(int i,int j)
{
if(i > j)
return i;
else
return j;
}
int longestpalind(string s) {
int n = s.size();
int dp[n][n];
// int(*dp)[n]; 动态分配数组空间
// dp = (int (*)[n])malloc(n*n*sizeof(int));
int temp;
int i,j,result;
for(i = 0;i< n;i++)
dp[i][i] = 1;
for(i = 1;i< n;i++) //子序列长度从小到大
{
temp = 0;
for(j = 0;j+i < n;j++)
{
if(s[j] == s[j+i])
temp = dp[j+1][j+i-1]+2;
else {
temp = Max(dp[j][j+i-1],dp[j+1][j+i]);
}
dp[j][j+i] = temp;
}
}
result = dp[0][n-1];
//free(dp);
return result;
}
int main()
{
string s = "google";
cout << longestpalind(s) << endl;
return 0;
}
在定义数组时需要考虑一个问题,数组的维数为变量,使用变长数组(VLA)或者是动态分配数组内存(malloc),在C99中加入了对变长数组的支持,在linux下的g++ 编译器是支持的,但是在有的编译器下是非法的,所以最好是通过动态分配内存的方法来定义数组。
变长数组的特点就是:自动存储,所用的内存空间再运行完定义部分之后会自动释放。对多维数组来说更方便。注意使用变长数组的定义时,如果出现数组访问越界,是不会报错的。
如果使用new来分配内存。
int **sp = new int *[n];
for(i = 0;inew int[n];
//释放时
for(i = 0;idelete [] sp[i];
delete []sp;
注意这里子串是连续的,仍然使用动态规划的方法。
1.dp[i][j]表示子串s[i…j]是否为回文
2.初始化:dp[i][j] = true (0,n-1);dp[i][i-1] = true(k= 2时使用)。
3.dp[i][j] =(s[i] == s[j] && dp[i+1][j-1] == true)
#include
#include
using namespace std;
int lps(string s)
{
int n = s.size();
bool dp[n][n];
int resleft= 0 ,resright =0;
int i,j;
dp[0][0] = true;
for(i = 1;i< n;i++)
{
dp[i][i] = true;
dp[i][i-1] = true; //k=2时使用
}
for(i = 2;i<=n;i++)
{
for(j = 0;j <= n-i;j++)
{
if(s[j] == s[j+i-1] && dp[j+1][j+i-2])
{
dp[j][j+i-1] = true;
if(resright -resleft +1 1;
}
}
}
}
int result = resright-resleft+1;
return result;
}
int main()
{
string s = "google";
cout << lps(s) << endl;
return 0;
}
方法二:编译回文子串的中心元素,分别计算偶数长度和奇数长度的回文子串。
#include
#include
using namespace std;
int lps(string s)
{
int n = s.size();
int maxlen = 0;
int i,j,start;
for(i = 1;i< n;i++)
{
int low = i-1; //以i-1,i为中心的回文
int high = i;
while(low >=0 && high < n&& s[low] == s[high])
{
low--;
high++;
}
if(high-low-1 > maxlen) //high-1-(low +1)+1 = len= high-low-1
{
maxlen = high-low-1;
start = low+1;
}
low = i-1; //以i为中心的回文
high = i+1;
while(low >=0 && high < n && s[low] == s[high])
{
low--;
high++;
}
if(high-low-1 > maxlen)
{
maxlen = high-low-1;
start = low+1;
}
}
return maxlen;
}
int main()
{
string s = "google";
cout << lps(s) << endl;
return 0;
}