Mail.Ru Cup 2018 Round 2: D. Refactoring(模拟+KMP)

 

题意:

给你n个初始字符串w_1~w_n,再给你n个最终字符串w_1^'~w_n^',问能不能找到两个字符串S和T,按照以下操作后,使得初始字符串和最终字符串一一对应相同,也就是对于所有的i,都满足w_i=w_i^'(输入保证长度对应相同,且一定存在一行字符串,初始和最终不同)

操作:对于每个串w_i,从左往右找到第一个子串S,将其替换成T,找不到就不进行修改

 

思路:

先随便找一个不满足w_i=w_i^'的字符串w_i,那么可以修改w_i的一段尽可能短的前缀使得w_i=w_i^',也就是可以暂时将当前的S和T作为答案,其中S是w_i的一段前缀,T是w_i^'的一段前缀,且|S| = |T|

然后只要依次检测的所有字符串按照以上操作能否全部满足w_i=w_i^'即可

如果某个字符串w_i无法修改成一致( 利用当前S和T按照上述操作后不能满足w_i=w_i^'),那么有三种情况:

  1. w_iw_i^'本相同,但是修改之后反而不同了
  2. w_iw_i^' 不同,然而w_i并不包含子串S,但是w_i包含子串S的一段后缀,且修改这段后缀即可满足w_i=w_i^'
  3. w_iw_i^' 不同,不仅w_i不包含子串S,w_i也不包含子串S的任何一段后缀,或者说修改了也仍然不可能满足w_i=w_i^'

对于情况③,找到直接就是NO了,没有任何办法,例如题目中的第三个样例

对于情况②,很显然可以将S和T同时去掉一段前缀,使得满足操作后w_i=w_i^',例如下面这个样例,正确答案应该是S = "bc",T = "bd"

2
abcpp
kbcpp
abdpp
kbdpp

对于情况③,可以将S和T的后面同时多加几个字母, 使得满足操作后w_i=w_i^',其中多加的字母很显然是固定的,例如下面这个样例,如果S = "abc",T = "aba",那么会修改到第2个字符串,所以答案应该是S = "abcd",T = "abad" 

2
abcd
abc
abad
abc

 

那么这题其实就可以搞定了,步骤如下

  1. 先单独检测一遍情况②,如果出现,暴力删除当前S和T的第一个字母直到满足条件为止,如果在删除某个字母之后前面某个字符串反而不能满足操作后w_i=w_i^'了,直接输出NO,结束
  2. 然后单独检测一遍情况①,如果出现, 暴力在S和T后面多接一个字母直到满足条件为止,如果在添加某个字母之后前面某个字符串反而不能满足操作后w_i=w_i^'了,直接输出NO,结束
  3. 最后再检测一遍情况③,出现直接就是NO
  4. YES,输出S和T

以上操作都可以在O(len)的复杂度内解决,其中len为所有字符串总长度,中间可能需要KMP

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
typedef struct Res
{
	int len, L, R, l, r;
	int net[3005];
	char s1[3005], s2[3005];
	void Getnext()
	{
		int i, j;
		i = l, j = l-1;
		net[l] = l-1;
		while(i<=r)
		{
			if(j==l-1 || s1[i]==s1[j])
			{
				i++, j++;
				if(s1[i]==s1[j])
					net[i] = net[j];
				else
					net[i] = j;
			}
			else
				j = net[j];
		}
	}
}Res;
Res a[3005], poi;
int Jud(Res c, Res d)
{
	int i, j, k, now, flag;
	char temp[3005];
	for(i=1;i<=c.len;i++)
		temp[i] = c.s1[i];
	temp[c.len+1] = flag = 0;
	i = 1, j = poi.l;
	while(i<=c.len)
	{
		if(temp[i]==poi.s1[j])
		{
			if(j==poi.r)
			{
				k = i-(poi.r-poi.l);
				for(now=poi.l;now<=poi.r;now++)
					temp[k++] = poi.s2[now];
				flag = 1;
				break;
			}
			i++, j++;
		}
		else
		{
			j = poi.net[j];
			if(j==poi.l-1)
				i++, j = poi.l;
		}
	}
	//printf("%s %s\n", temp+1, c.s2+1);
	if(strcmp(temp+1, c.s2+1)==0)
		return 1;		//一样
	if(flag)
		return -1;		//修改过,但还是不一样
	return 0;			//没有被修改,不一样
}
int main(void)
{
	int n, i, now;
	scanf("%d", &n);
	for(i=1;i<=n;i++)
	{
		scanf("%s", a[i].s1+1);
		a[i].len = strlen(a[i].s1+1);
	}
	for(i=1;i<=n;i++)
		scanf("%s", a[i].s2+1);
	for(i=1;i<=n;i++)
	{
		if(strcmp(a[i].s1+1, a[i].s2+1)!=0)
		{
			swap(a[1], a[i]);
			break;
		}
	}
	poi = a[1];
	for(i=a[1].len;i>=1;i--)
	{
		if(poi.s1[i]!=poi.s2[i])
			poi.L = i;
	}
	for(i=1;i<=poi.len;i++)
	{
		if(poi.s1[i]!=poi.s2[i])
			poi.R = i;
	}
	poi.l = 1, poi.r = poi.R;
	poi.Getnext();
	for(i=1;i<=n;i++)
	{
		now = Jud(a[i], poi);
		while(now!=1 && poi.l!=poi.L)
		{
			if(now==0)
			{
				poi.l++;
				poi.Getnext();
				now = Jud(a[i], poi);
			}
			else
				break;
		}
	}
	for(i=1;i<=n;i++)
	{
		now = Jud(a[i], poi);
		while(now!=1 && poi.r!=poi.len)
		{
			if(now==-1)
			{
				poi.r++;
				poi.Getnext();
				now = Jud(a[i], poi);
			}
			else
				break;
		}
	}
	for(i=1;i<=n;i++)
	{
		if(Jud(a[i], poi)!=1)
		{
			printf("NO\n");
			return 0;
		}
	}
	printf("YES\n");
	for(i=poi.l;i<=poi.r;i++)
		printf("%c", poi.s1[i]);
	puts("");
	for(i=poi.l;i<=poi.r;i++)
		printf("%c", poi.s2[i]);
	puts("");
	return 0;
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值