CodeForces - 49E Common ancestor(dp)

本文深入探讨了一个涉及字符串替换规则的问题,通过区间动态规划(DP)和矢量数据结构,解决两个字符串经过一系列替换后形成相同最短串长度的计算。详细介绍了算法流程,包括状态转移方程和DP矩阵的应用,最终通过实例代码实现了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:有n个替换规则,2个字母可以被替换成一个字母,给出2个串,问这2个串经过一系列的替换后形成的最短且相同的串长度是多少。

做法:我们可以先用区间dp,d[i][j][k]代表i到j区间是否可以变成k这个字母。再利用vector去存一下起点为i,变成k这个字母的终点为哪些,然后就可以进行后来的dp了。设dp[i][j]为第一个串用了i个字母,第二个串用了j个字母经过替换变成相同的串的最短长度。转移就是枚举下一个字母是什么,对于某个dp[i][j],第一个串起点就是i+1,需要利用刚才的vector去找变成枚举的字母的终点。第二个串就是起点是j+1,一样。更新dp[x][y] = min(dp[x][y], dp[i][j]+1)。最后答案就是dp[len1][len2]。

AC代码:

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned long long
#define eps 1e-8
#define NMAX 10000000
#define MOD 1000000007
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
#define mp make_pair
template<class T>
inline void scan_d(T &ret)
{
    char c;
    int flag = 0;
    ret=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c == '-')
    {
        flag = 1;
        c = getchar();
    }
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
    if(flag) ret = -ret;
}

char s1[55],s2[55],tmp[10];
int sub[55][3],n;
bool dp1[55][55][30],dp2[55][55][30];
vector<int>v1[55][30],v2[55][30];
vector<int>::iterator it1,it2;
void getdp(char *s, int len, bool dp[][55][30], vector<int> v[][30])
{
    for(int i = 1; i <= len; i++)
        dp[i][i][s[i]-'a'] = 1;
    for(int L = 2; L <= len; L++)
        for(int i = 1; i + L - 1 <= len; i++)
            for(int k = i; k < i+L-1; k++)
                for(int j = 1; j <= n; j++)
                    dp[i][i+L-1][sub[j][2]] |= dp[i][k][sub[j][0]]&&dp[k+1][i+L-1][sub[j][1]];
    for(int i = 1; i <= len; i++)
        for(int j = i; j <= len; j++)
            for(int k = 0; k < 26; k++) if(dp[i][j][k])
                v[i][k].push_back(j);
}

int dp[55][55];
int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o.txt","w",stdout);
#endif
    scanf("%s%s",s1+1,s2+1);
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%s",tmp);
        sub[i][0] = tmp[3]-'a';
        sub[i][1] = tmp[4]-'a';
        sub[i][2] = tmp[0]-'a';
    }
    int len1 = strlen(s1+1), len2 = strlen(s2+1);
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    for(int i = 1; i <= 55; i++)
        for(int j = 0; j < 30; j++)
        {
            v1[i][j].clear();
            v2[i][j].clear();
        }
    getdp(s1,len1,dp1,v1);
    getdp(s2,len2,dp2,v2);
    for(int i = 0; i <= len1; i++)
        for(int j = 0; j <= len2; j++)
            dp[i][j] = 100;
    dp[0][0] = 0;
    for(int i = 0; i < len1; i++)
        for(int j = 0; j < len2; j++) if(dp[i][j] != 100)
            for(int k = 0; k < 26; k++)
                for(it1 = v1[i+1][k].begin(); it1 != v1[i+1][k].end(); it1++)
                    for(it2 = v2[j+1][k].begin(); it2 != v2[j+1][k].end(); it2++)
                        dp[*it1][*it2] = min(dp[*it1][*it2],dp[i][j]+1);
    if(dp[len1][len2] == 100) printf("-1\n");
    else printf("%d\n",dp[len1][len2]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值