题意: 输入M个定长为60的DNA序列(字符串),求M条DNA的最长公共子序列,若有最长的公共子序列有若干条,则输出字典序最小的。
显然是一个字符串匹配问题。第一次用KMP算法。幸好看了严蔚敏老师的数据结构视频(第11,12),讲的很细。
题解:需要注意输出字典序最小的模式序列
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
#define M 65
struct node { char str[M]; };
char DNA[15][M], res[M];
int next[M], n, m, flag;
stack<node> S;
void make_pattern ()
{
node temp;
while ( ! S.empty() ) S.pop();
/* 先从长度3开始枚举可能的模式序列,直至长度60,并存放入栈*/
for ( int pl = 3; pl <= 60; pl++ )
{
for ( int i = 0; i <= 57; i++ )
{
if ( i + pl > 60 )
break;
strncpy(temp.str,DNA[0]+i,pl);
temp.str[pl] = '\0';
S.push(temp);
}
}
}
void get_Next ( char p[] ) //数组可以进一步优化,但是现在懒得写了
{
next[0] = -1;
int i = 0, j = -1, len=strlen(p);
while ( i < len )
{
if ( j == -1 || p[i] == p[j] )
{++i; ++j;next[i] = j;}
else j = next[j];
}
}
/*检测某一模式序列是否是所有DNA的公共子序列,函数返回布尔值*/
bool KMP ( char p[] )
{
get_Next(p);
int i,j,k,len=strlen(p);
for ( k = 1; k < m; k++ )
{
i = j = 0;
while ( i < 60 && j < len )
{
if ( j == -1 || DNA[k][i] == p[j] )
{ ++i; ++j; }
else
{ j = next[j];};
}
if ( j < len )
return false;
}
/*进行到这一步说明模式序列P是所有DNA的公共子序列*/
if ( strcmp(p,res) < 0 || res[0] == '\0' )
/*比较P与原来的结果,取字典序小的*/
strcpy(res,p);
return true;
}
int main()
{
scanf("%d",&n);
while ( n-- )
{
scanf("%d",&m);
for ( int i = 0; i < m; i++ )
scanf("%s",&DNA[i]);
res[0] = '\0';
flag = false;
make_pattern();
while ( ! S.empty() )
{
node pat;
pat = S.top(); S.pop();
if ( flag )
{
/*flag=true说明已经匹配到公共子序列,即已经产生了一个可行的结果。若当前的模式序列pat.str长度小于已求出的的结果,
则可以跳出循环了,因为子序列入栈时是从短到长,所以出栈是从长到短,后面的长度一定更小。否则继续匹配,检测是否有字典序较小的*/
if ( strlen(pat.str) < strlen(res) )
break;
else KMP(pat.str);
}
else flag = KMP (pat.str);
}
if( flag )
printf("%s\n", res);
else
printf("no significant commonalities\n");
}
return 0;
}