【gomj】【DP】 小学生语文题

【gomj】【DP】 小学生语文题

题目

在这里插入图片描述
在这里插入图片描述


解题思路

设f[i][j]为第一段的 i~n 能从第二段的 j~n 中匹配出来
转移分为三种情况

  • x[i]==y[j] 当前俩字符刚好一样,直接从[i+1][j+1]转移 已经一样 所以不需要操作
  • x[i]能在y[j~n]中找到且没用过 从f[i+1][j]转移 能在之前的匹配段中找到 说明已经用过操作了
  • 当前字符不一样 且不能在之前的匹配段找到 说明 j 往前了仍未匹配到x[i] 进行操作 从f[i][j+1]+1转移

设g[i][j][0/1]记录从哪个状态转移到当前的
找到俩字符相同的情况
然后将不是俩字符相同的情况的字符出现记录下来
统计答案时 如果是俩字符相同的情况 但当前匹配不一样 那么就记录下来具体从哪拿
如果最后还剩有字符 就都往第一位扔


代码

#include<iostream>
#include<cstring> 
#include<cstdio>
using namespace std;
int w,n,f[2010][2010],g[2010][2010][3];
int p[2][2010][30],zc[30][2010],d[2010],ans[2010][2];
char x[2010],y[2010];
int main()
{
	freopen("chinese.in","r",stdin);
	freopen("chinese.out","w",stdout);
	scanf("%d",&w);
	while (w--)
	{
		memset(g,0,sizeof(g));
		memset(zc,0,sizeof(zc));
		memset(p,0,sizeof(p));
		memset(f,127,sizeof(f));
		memset(d,0,sizeof(d));
		memset(ans,0,sizeof(ans));
		scanf("%s%s",x+1,y+1);
		n=strlen(x+1);
		for (int i=n;i>0;i--)
		{
			for (int j=0;j<26;j++)
			{
				p[0][i][j]=p[0][i+1][j];
				p[1][i][j]=p[1][i+1][j];
			}
			p[0][i][x[i]-'a']++;
			p[1][i][y[i]-'a']++;  //统计出现次数
		}
	    for (int i=n+1;i>0;i--)
	        f[n+1][i]=n+1-i;  //赋初值
	    for (int i=n;i>0;i--)
	    {
	         for (int j=n;j>0;j--)
	         {
	         	 if (f[i+1][j]!=f[0][0]&&p[1][j][x[i]-'a']-p[0][i+1][x[i]-'a']>0&&f[i][j]>f[i+1][j])
	         	 {
	         	     f[i][j]=f[i+1][j];
	         	     g[i][j][0]=i+1,g[i][j][1]=j;
				 }
				 if (f[i+1][j+1]!=f[0][0]&&x[i]==y[j]&&f[i][j]>f[i+1][j+1])
				 {
				 	 f[i][j]=f[i+1][j+1];
				 	 g[i][j][0]=i+1,g[i][j][1]=j+1;
				 }
			 }
			 for (int j=n;j>0;j--)
			     if (f[i][j+1]!=f[0][0]&&f[i][j]>f[i][j+1]+1) 
				 {
				 	f[i][j]=f[i][j+1]+1;
				 	g[i][j][0]=i,g[i][j][1]=j+1;
				 } 
		}
		    
		printf("%d\n",f[1][1]);
		int l=1,r=1,hou=n,t=0;
		while (l!=n+1)
		{
			  while (g[l][r][0]==l) r=g[l][r][1];
			  if (g[l][r][1]!=r) d[r]++,r++;
			   l++; 
		}
		for (int i=1;i<=n;i++)
		{
	        d[i]=d[i]^1;  
			if (d[i])
			{
			   int k=y[i]-'a';
				zc[k][++zc[k][0]]=i; 
			}
		}
		for (int i=n;i>0;i--)
		{
			if (!hou) break;
			if (!d[i])
			{ 
			   	while (x[hou]!=y[i])
				{
					  int k=x[hou]-'a';
					   ans[++t][0]=zc[k][zc[k][0]--];  
					   ans[t][1]=i+1;
					   hou--;
				}    
				hou--;
			}  
		} 
		for (int i=hou;i>0;i--)
		{
			int k=x[i]-'a';
			ans[++t][0]=zc[k][zc[k][0]--];
			ans[t][1]=1;
		}
		for (int i=1;i<t;i++)
		    for (int j=i+1;j<=t;j++)
		    {
		    	if (ans[i][0]>=ans[j][0]&&ans[j][0]>=ans[i][1]) ans[j][0]++;
		        if (ans[i][0]>ans[j][1]&&ans[j][1]>ans[i][1]) ans[j][1]++;
			} 
		for (int i=1;i<=t;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值