【JZOJ5102】【GDOI2017 day2】小学生语文题

本文介绍了一种使用倒序动态规划方法解决字符串匹配问题的算法,并详细解释了状态定义、转移方程及如何输出匹配过程的具体实现。

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

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

这道题考场上没想到可以倒着dp,感觉dp都白学了。
我们设出f[i][j]表示当前正确串匹配到i,原串匹配到j,i正对着j,i-n中的每个字符j-n中都可匹配的最小步数。我们现在考虑转移。
1、假设当前的j+1我们对其进行操作使它往前丢,那么f[i][j]=f[i][j+1]+1。
2、假设当前的s[i]==s1[j],那么f[i][j]=f[i+1][j+1]。
3、假设之前往前丢的点中存在s[i],就让他丢到这里,因为那么f[i][j]=f[i+1][j].
问题是如何输出方案。
我们可以设出g[i][j]表示更新f[i][j]的是哪个点,根据上面的定义确定该点假如是要插入,就把它打上标记。我们从后往前遍历,遇到不匹配的就看他是否有标记,没有就把它丢入桶中,有就从桶中取出字符。最后我们还要处理相对位置问题,暴力的O(N^2)判一下假如发现一个点在前面输出的操作之间就+1。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e3+5;
struct code{
    int x,y;
}g[maxn][maxn],ans1[maxn];
int f[maxn][maxn],c[maxn][27],d[maxn][27],sum[27],bz[maxn],q[27],g1[maxn];
int m,n,i,t,j,k,l,x,y,z,xx,yy,num,num1,mi;
char s[maxn],s1[maxn];
int main(){
    freopen("chinese.in","r",stdin);freopen("chinese.out","w",stdout);
    scanf("%d\n",&m);
    while (m){m--;
        scanf("%s\n",s+1);
        scanf("%s\n",s1+1);n=strlen(s+1);
        memset(c,0,sizeof(c));memset(d,0,sizeof(d));memset(g1,0,sizeof(g1));
        memset(q,0,sizeof(q));memset(sum,0,sizeof(sum));memset(f,127,sizeof(f));
        for (i=n;i>=1;i--){
            for (j=0;j<26;j++)
                d[i][j]=d[i+1][j],c[i][j]=c[i+1][j];
            d[i][s[i]-97]++;c[i][s1[i]-97]++;
        }
        for (i=1;i<=n+1;i++)f[n+1][i]=n+1-i,g[n+1][i].x=n+1,g[n+1][i].y=i+1;
        for (i=n;i>=1;i--){
            for (j=i;j>=1;j--){
                f[i][j]=f[i][j+1]+1,g[i][j].x=i,g[i][j].y=j+1;
                if (d[i][s[i]-97]<=c[j][s[i]-97]&&f[i+1][j]<f[i][j]) f[i][j]=f[i+1][j],g[i][j].x=i+1,g[i][j].y=j;
                if (s[i]==s1[j]&&f[i+1][j+1]<f[i][j]) f[i][j]=f[i+1][j+1],g[i][j].x=i+1,g[i][j].y=j+1;
            }
        }
        printf("%d\n",f[1][1]);
        x=1;y=1;memset(bz,0,sizeof(bz));
        while (f[x][y]){
            xx=g[x][y].x;yy=g[x][y].y; 
            if (xx==x+1 && yy==y) bz[x]=1;
            x=xx;y=yy;
        }k=0;j=n;i=n;num=0;
        while (j){
            if (s1[i]==s[j]){
                i--;j--;continue;
            }
            if (!bz[j]) f[s1[i]-97][++sum[s1[i]-97]]=i,i--;
            else{
                ans1[++num].x=f[s[j]-97][++q[s[j]-97]],ans1[num].y=i+1;
                j--;k++;
            }
        }
        for (i=1;i<=num;i++)
            for (j=i+1;j<=num;j++)
                if (ans1[i].x>ans1[j].x && ans1[i].y<ans1[j].x) ans1[j].x++;
        for (i=1;i<=num;i++)
            printf("%d %d\n",ans1[i].x,ans1[i].y);
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值