题意:
分析:
看错题意了,导致我一直认为有一种 ((n^2k)) 的做法/kk
-
正解:
我们发现,公共祖先的每一个字母,一定对应着最终串的一段连续的区间,所以我们反向构造,利用区间DP反推出这段区间对应的字母,然后找出最短的一种
具体来说就是:
我们记 (f_{i,j,k}) 表示第 (i) 到第 (j) 个字母对应的区间能否变成 (k),转移就是
f[l][r][k]|=f[l][mid][x]&&f[mid+1][r][y]&&trans[x][y]==k
,但是我们发现这样枚举的复杂度是 (O(n^326^3)) ,大部分枚举的字母无法转移,所以我们把枚举字母优化掉,直接提前存一下每个字母对应的转移,这样复杂度是 (O(n^426)) 的得到区间信息后我们考虑怎么得到答案,我们记 (g_{x,y}) 表示第一个串枚举到 (x) 第二个串枚举到 (y) 时的最小代价,转移:
if(f1[x][i][c]==f2[y][j][c]) g[i][j]=min(g[i][j],g[x-1][y-1]+1)
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
typedef long long ll;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n;
bool f[2][55][55][26];
int g[55][55],s[2][55],length[2];
char ch[55];
vector<pii> c[26];
void work()
{
for(int id=0;id<=1;id++) { scanf("%s",ch+1);n=strlen(ch+1);length[id]=n;for(int i=1;i<=n;i++) s[id][i]=ch[i]-'a';}
n=read();
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
c[ch[1]-'a'].pb(mk(ch[4]-'a',ch[5]-'a'));
}
for(int id=0;id<=1;id++)
{
for(int i=1;i<=length[id];i++) f[id][i][i][s[id][i]]=true;
for(int len=1;len<=length[id];len++)
for(int l=1,r;l+len-1<=length[id];l++)
{
r=l+len-1;
for(int mid=l;mid<r;mid++) for(int k=0;k<=25;k++) for(auto x:c[k])
f[id][l][r][k]|=(f[id][l][mid][x.fir]&&f[id][mid+1][r][x.sec]);
}
}
memset(g,0x3f,sizeof(g));
g[0][0]=0;
for(int i=1;i<=length[0];i++) for(int j=1;j<=length[1];j++) for(int x=1;x<=i;x++) for(int y=1;y<=j;y++) for(int k=0;k<=25;k++)
if(f[0][x][i][k]&&f[1][y][j][k]) g[i][j]=min(g[i][j],g[x-1][y-1]+1);
if(g[length[0]][length[1]]>50) g[length[0]][length[1]]=-1;
printf("%d
",g[length[0]][length[1]]);
}
}
int main()
{
zzc::work();
return 0;
}