题目大意:给出两个串求最长的公共子序列。最长公共子序列好求,难得是输出。
暴力dfs。n*n!跪了,主要是两个串如果大量的字符相同,dfs次数太多,TLE是必然的。
显然必须换姿势了。各位大牛给的是枚举26个字母。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ma 100
int data[ma][ma]={0};
char s1[ma]={},s2[ma]={};
int len1,len2;
int an;
char s[ma]={};
int coun = 0;
struct node { char a[100];};
node answer[100009]={};
bool cmp(node a,node b)
{
if(strcmp(a.a,b.a))
return true;
return false;
}
void dfs(int i,int j,int sum)
{
printf("%d %d\n",i,j);
if(sum == an)
{
strcpy(answer[coun++].a,s);
return ;
}
if(i > 0 && j > 0 && data[i-1][j-1] > sum) // 两个串相同,这个剪枝无效!所以一直TLE
return ;
for(int x = i; x < len1; x++) // 如果两个串相同,不停的调用dfs. 相当于 n!数据打了,坑定会TLE的
for(int y = j;y < len2; y++)
if(s1[x] == s2[y])
{ s[sum] = s1[x]; dfs(x+1,y+1,sum+1);}
return ;
}
int main()
{
scanf(" %s %s",s1+1,s2+1);
s1[0] =s2[0]= 'a'; len1 = strlen(s1);len2 = strlen(s2);
for(int i = 1; i < len1; i++)
for(int j = 1; j < len2; j++)
if(s1[i] == s2[j])
data[i][j] = data[i-1][j-1] + 1;
else
{
if(data[i-1][j] >= data[i][j-1])
data[i][j] = data[i-1][j];
else
data[i][j] = data[i][j-1];
}
an = data[len1-1][len2-1];
for(int i = 1; i < len1; i++)
for(int j = 1; j < len2; j++)
if(s1[i] == s2[j] && data[i][j] == 1)
dfs(i,j,0);
//sort(answer,answer+coun,cmp);
/*for(int i = 0; i < coun; i++)
if(strcmp(answer[i].a,answer[i+1].a) != 0)
printf("%s\n",answer[i].a);*/
return 0;
}
http://blog.youkuaiyun.com/tsaid/article/details/6726698这个的(我的语文不好复制过来的)。
1.用 pos1[ i ] [ j ] 代表字符 'a'+j 在 s1 中第 i 个字符前最后出现的位置 ( 即 1 -- i 这个范围内,最后出现的位置 )。
若字符 'a' + j 不存在,那么pos1 [ i ] [ j ] = 0;
pos2 同理。
2.枚举最长公共子序列的每一个字符。假设最长公共子序列的长度为 cnt, s1 长度为 l1, s2 长度为 l2。 我们先确定第 cnt 个字符, dfs ( l1, l2, cnt )。
它可能是 a, b, c, ····,z 的其中之一。假设是 a, 那么需要找出 a 在 s1 及 a在 s2 中最后出现的位置 pos1 [ l1 ] [ 0 ], pos2 [ l2 ] [ 0 ], 令 p1 = pos1[l1][0], p2 = pos2[l2][0]。 若 dp[p1][p2] == cnt ,那么 ans [ cnt ] = a,
在此基础上枚举第 cnt - 1 个字符, dfs ( p1 - 1, p2 - 1, cnt - 1 )。·····最终找到第 1 个字符,结束。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define N 90
char s1[N]={},s2[N]={};
int dp[N][N]={},pos1[N][30]={},pos2[N][30]={};
struct item
{ char str[N];}com;
bool operator > (const item &a,const item &b)
{
if(strcmp(a.str,b.str) > 0)
return true;
return false;
}
priority_queue<item,vector<item>,greater<item> >qu;
void dfs(int l1,int l2,int c)
{
if(c < 1)
{ qu.push(com);return ;}if(l1 < 1 || l2 < 1) return ;
for(int i = 0; i < 26; i++) //枚举26个字母,不会产生相同的序列。
{
int t1 = pos1[l1][i];
int t2 = pos2[l2][i];
if(dp[t1][t2] == c) //优化。
{
com.str[c-1] = 'a' + i;
//printf("%s\n",com.str);
dfs(t1-1,t2-1,c-1);
}
}
}
int main()
{
int len1,len2;
scanf("%s%s",s1+1,s2+1);
len1 = strlen(s1+1);
len2 = strlen(s2+1);
for(int i = 1; i <= len1; i++)
for(int j = 1; j <= len2; j++)
if(s1[i] == s2[j])
dp[i][j] = dp[i-1][j-1]+1;
else
{
if(dp[i][j-1] > dp[i-1][j])
dp[i][j] = dp[i][j-1];
else
dp[i][j] = dp[i-1][j];
}
//printf("%d\n",dp[len1][len2]);
for(int i =1; i <= len1; i++)
for(int j = 0; j < 26; j++)
if(s1[i] == 'a' + j)
pos1[i][j] = i;
else
pos1[i][j] = pos1[i-1][j];
for(int i = 1; i <= len2; i++)
for(int j = 0; j < 26; j++)
if(s2[i] == 'a' + j)
pos2[i][j] = i;
else
pos2[i][j] = pos2[i-1][j];
com.str[dp[len1][len2]] = '\0';
dfs(len1,len2,dp[len1][len2]);
while(!qu.empty())
{
printf("%s\n",qu.top().str);
qu.pop();
}
return 0;
}