1、两个字符串中最长公共子序列
该问题是动态规划的经典问题,找出的公共序列不一定是连续的,参考资料很多,这里就贴出公式和代码,做个简单的笔记。
设序列a,b的长度分别为n和m,L(i,j)为a(i),b(j)的最长公共子序列长度,有递推公式:
时间复杂度为O(mn),代码如下:
#define N 50
intlcs(char *a, char *b, char *c)
{
inti,j,k;
intn,m;
intmz[N+1][N+1],s[N+1][N+1];
n=strlen(a);
m=strlen(b);
memset(mz,0,sizeof(mz));
memset(s,0,sizeof(s));
for(i=1;i<n+1;i++)
{
for(j=1;j<m+1;j++)
{
if(a[i-1]==b[j-1])
{
mz[i][j]=mz[i-1][j-1]+1;
s[i][j]=0;
}
else
{
if(mz[i-1][j]>=mz[i][j-1])
{
mz[i][j]=mz[i-1][j]; //说明a[i]不在公共子序列中
s[i][j]=-1;
}
else
{
mz[i][j]=mz[i][j-1]; //说明b[i]不在公共子序列中
s[i][j]=1;
}
}
}
}
i=n,j=m;
k=mz[n][m];
c[k--]='\0';
while(i>0 && j>0)
{
switch(s[i][j])
{
case -1:
i--;
break;
case 0:
c[k--]=a[i-1];
i--,j--;
break;
case 1:
j--;
break;
default:
break;
}
}
returnmz[n][m];
}
2、两个字符串中最长公共子串
与公共子序列不同,公共子串是连续的,在使用动态规划求解2个长度分别为p, q的字符串S,T的最长公共子串问题前,先给出求它们任意前缀子串对S[1:i],T[1:j]的最长公共后缀的算法,其中:1 ≤ i ≤ p,1 ≤ j ≤ q。设LCS(S[1:i],T[1:j])表示前缀子串对S[1: i] ,T[1:j] 的最长公共后缀,递推关系式如下:
LCS(S[1:i],T[1,j])=LCS(S[1:i],T[1:j])+S[i] S[i]=T[j]
=”” S[i]!=T[j]
参考文献:http://blog.youkuaiyun.com/liufeng_king/article/details/8528858
代码如下:
string getLCS(string &s, string&t)
{
int q=s.length();
int p=t.length();
int i,j;
string **num = new string *[q];
for(i=0;i<q;i++)
{
num[i] = new string[q];
}
char char1 = '\0';
char char2 = '\0';
int len = 0;
string lcs = "" ;
for(i=0; i<q; i++)
{
for (j=0; j<p; j++)
{
char1 = s.at(i);
char2 = t.at(j);
if(char1!=char2)
{
num[i][j] = "" ;
}
else
{
if (i==0||j==0)
{
num[i][j]=char1;
}
else
{
num[i][j]=num[i-1][j-1]+char1;
}
if(num[i][j].length()>len)
{
len = num[i][j].length() ;
lcs = num[i][j];
}
else if(num[i][j].length()==len)
{
lcs = lcs + "," + num[i][j];
}
}
}
}
for(i=0;i<q;i++)
{
delete[] num[i];
}
delete[] num;
return lcs;
}
3、最长回文子串
有了最长公共子串的方法,那么求字符串中最长回文子串也变得简单了,只需要把原字符串反转,然后与求最长公共子串即可。代码如下:
//回文字符串
string getLCS(string &s)
{
int q=s.length();
string t=s; //反转字符串,找出s,t的最大公共子串
int i,j;
for (i = 0; i<q/2; i++)
{
char temp = t[i];
t[i] = t[q-i-1];
t[q-i-1] = temp;
}
string **num = new string *[q];
for(i=0;i<q;i++)
{
num[i] = new string[q];
}
char char1 = '\0';
char char2 = '\0';
int len = 0;
string lcs = "" ;
for(i=0; i<q; i++)
{
for (j=0; j<q; j++)
{
char1 = s.at(i);
char2 = t.at(j);
if(char1!=char2)
{
num[i][j] = "" ;
}
else
{
if (i==0||j==0)
{
num[i][j]=char1;
}
else
{
num[i][j]=num[i-1][j-1]+char1;
}
if(num[i][j].length()>len)
{
len = num[i][j].length() ;
lcs = num[i][j];
}
else if(num[i][j].length()==len)
{
lcs = lcs + "," + num[i][j];
}
}
}
}
for(i=0;i<q;i++)
{
delete[] num[i];
}
delete[] num;
return lcs;
}
时间复杂度O(n^2),当然还有一种复杂度只有O(n)方法,详见参考文献:
http://www.akalin.cx/longest-palindrome-linear-time
http://bbs.dlut.edu.cn/bbstcon.php?board=Competition&gid=23474
http://blog.youkuaiyun.com/sgbfblog/article/details/7979541