题目来自于2014.8.29阿里在线笔试题:给定一个query和一个text,均由小写字母组成。要求在text中找出以同样的顺序连续出现在query中的最长连续字母序列的长度。例如, query为“acbac”,text为“acaccbabb”,那么text中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3。请注意程序效率。
仔细分析这道题,发现就是求query和text的最长公共子串长度,是经典的动态规划问题。
设F(i,j)表示以query[i]和text[j]为结尾的公共子串长度,如果query[i]!=text[j],即两个字符不等,显然不会有以两者为共同结尾的子串,因此F(i,j)=0;如果query[i]==text[j],即两个字符相等,就应该判断两者前一个字符是否相等,如果不等则说明只有这两个字符是公共子串,因此F(i,j)=1,如果相等,公共子串长度应为前一个字符的公共子串长度加1,因此F(i,j)=F(i-1,j-1)+1。
综上可知状态转移方程为:
F(i,j) = 0 ( query[i]!=text[j] )
= F(i-1,j-1)+1 (query[i]==text[j] )
实际编程时,可开辟Q*T大小的二维数组存储计算结果,再从所有结果中取出最大值即可。
复杂度分析:Q为query长度,T为text长度,需要把每个字符两两进行比较,时间复杂度为O(Q*T),需要存储所有字符的比较结果,空间复杂度为O(Q*T)
代码如下:
/*
给定一个query和一个text,均由小写字母组成。要求在text中找出以同样的顺序连续出现在
query中的最长连续字母序列的长度。例如, query为“acbac”,text为“acaccbabb”,那么text
中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3。请注意程
序效率。
*/
#include <iostream>
#include <string>
using namespace std;
int findMaxLenDP( const string& query , const string& text )
{
int Qlen = query.length();
int Tlen = text.length();
int** F = new int*[Qlen];//二维数组,存储所有公共子串长度
for( int i=0 ; i<Qlen ; i++ )
F[i] = new int[Tlen];
for( int i=0 ; i<Qlen ; i++ )
{
for( int j=0 ; j<Tlen ; j++ )
F[i][j]=0;
}
//初始化第一行和第一列
for( int i=0 ; i<Qlen ; i++ )
{
if( query[i] == text[0] )
F[i][0] = 1;
}
for( int j=0 ; j<Tlen ; j++ )
{
if( text[j] == query[0] )
F[0][j] = 1;
}
int maxSublen = 0;//存储最大公共子串长度
for( int i=1 ; i<Qlen ; i++ )
{
for( int j=1 ; j<Tlen ; j++ )
{
//状态转移方程
if( query[i] == text[j] )
{
F[i][j] = F[i-1][j-1]+1;
maxSublen = F[i][j]>maxSublen ? F[i][j] : maxSublen;
}
else
F[i][j] = 0;
}
}
for( int i=0 ; i<Qlen ; i++ )
delete[] F[i];
delete[] F;
return maxSublen;
}
int main()
{
cout << findMaxLenDP( "acbac" , "acaccbabb" ) << endl;
system("pause");
}