#2823. 「BalticOI 2014 Day 1」三个朋友

该文章介绍了一个字符串处理问题,给定一个字符串U,需要通过其构造原字符串S。题目要求首先将S复制一次并插入一个字符得到U,然后反向操作恢复S。文章通过哈希和字符串操作来解决此问题,涉及到字符串的复制、哈希值计算、字符删除等步骤,并提供了AC代码示例。
摘要由CSDN通过智能技术生成

题目描述

本题译自 BalticOI 2014 Day1 T2「Three Friends」

给定一个字符串 S,先将字符串 S 复制一次(变成双倍快乐),得到字符串 T,然后在 T 中插入一个字符,得到字符串 U。

给出字符串 U,重新构造出字符串 S。

所有字符串只包含大写英文字母。

输入格式

第一行一个整数 N,表示字符串 U 的长度。

第二行一个长度为 N 的字符串,表示字符串 U。

输出格式
一行一个字符串,表示字符串 S。

特别地:

如果字符串无法按照上述方法构造出来,输出 NOT POSSIBLE
如果字符串 S 不唯一,输出 NOT UNIQUE

样例 1

输入

1 7
2 ABCXABC

输出

1 ABC

样例 2

输入

1 6
2 ABCDEF

输出

1 NOT POSSIBLE

样例 3

输入

1 9
2 ABABABABA

输出

1 NOT UNIQUE

数据范围与提示

2 < = n < = 20000002 2<=n<=20000002 2<=n<=20000002

ACcode

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
char s[20000009];
string r_s1,r_s2,l_s1,l_s2;//r_s1 l_s1表示处理前字符串U的右半段和左半段,r_s2 l_s2表示删去字符后的右半段和左半段 
int n,b=1311,r_Hash,l_Hash,ans;//r_Hash表示字符串U右半段的哈希值,l_Hash表示左半段的哈希值 
ULL poww[20000009],Hash[20000009];
ULL _Hash(int l,int r)
{
	return Hash[r]-Hash[l-1]*poww[r-l+1];
}//求指定区域的哈希值 
ULL del(int l,int r,int k)
{
	return _Hash(l,k-1)*poww[r-k]+_Hash(k+1,r);
}//删去第k个字符,重新求哈希值 
int main()
{
	cin>>n;
	poww[0]=1;
	for(int i=1;i<=n;i++)
	 	poww[i]=poww[i-1]*b;//预处理 
	scanf("%s",s+1);
	if(n%2==0)//特判,n的长度必为奇数,因为字符串U是由一个字符串复制两次插入一个字符得到的 
	{
		cout<<"NOT POSSIBLE";
		return 0;
	}
	int mid=(n+1)/2;
	for(int i=1;i<=n;i++)
		Hash[i]=Hash[i-1]*b+(ULL)(s[i]-'A'+1);//预处理
	r_Hash=_Hash(mid+1,n);//求右半段哈希值 
	for(int i=mid+1;i<=n;i++)
		r_s1.push_back(s[i]);
	for(int i=1;i<=mid;i++)//左半段依次删去第i个字符,与右半段比较 
	{
		l_Hash=del(1,mid,i);
		if(l_Hash==r_Hash)//左右半段相同 
		{
			ans++;//答案+1 
			r_s2=r_s1;//记录处理后的右半段 
			break;
		}
	}
	l_Hash=_Hash(1,mid-1);//求左半段哈希值 
	for(int i=1;i<mid;i++)
		l_s1.push_back(s[i]);
	for(int i=mid;i<=n;i++)//右半段依次删去第i个字符,与左半段比较,循环内容同上 
	{
		r_Hash=del(mid,n,i);
		if(r_Hash==l_Hash)
		{
			ans++;
			l_s2=l_s1;
			break; 
		}
	}
	if(ans==0) cout<<"NOT POSSIBLE";//没有答案 
	else 
		if(ans==1||r_s2==l_s2)
		{
			if(r_s2.size()==0) cout<<l_s2;
			else cout<<r_s2;
		}//输出处理后不为空的半段,即是答案 
	else cout<<"NOT UNIQUE";//有多个答案 
	return 0;
}

思路:

首先将字符串U分为1 ~ mid-1 和 mid+1 ~ n,分别求出这两段的哈希值(l_Hash和r_Hash),然后枚举右段mid~n,依次删去第i个字符,求出删去第i个字符后这一段的哈希值,与l_Hash比较,若相同,ans++

1.特判

	if(n%2==0)
	{
		cout<<"NOT POSSIBLE";
		return 0;
	}

n的长度一定为奇数,因为字符串U是由一个字符串复制两次插入一个字符得到的,若n为偶数,直接输出 NOT POSSIBLE,然后return 0

2.两个函数

ULL _Hash(int l,int r)
{
	return Hash[r]-Hash[l-1]*poww[r-l+1];
}
ULL del(int l,int r,int k)
{
	return _Hash(l,k-1)*poww[r-k]+_Hash(k+1,r);
}

①_Hash函数是用来求 左端点为 l,右端点为 r 的子串的哈希值
②del函数是删去第k个字符后,用来求 左端点为 l,右端点为 r 的子串的哈希值

3.输出

	if(ans==0) cout<<"NOT POSSIBLE";
	else 
		if(ans==1||r_s2==l_s2)
		{
			if(r_s2.size()==0) cout<<l_s2;
			else cout<<r_s2;
		} 
	else cout<<"NOT UNIQUE";

①如果没有答案,输出NOT POSSIBLE
②否则:如果有一个答案 或者 两个串删去字符后相等 输出非空的串
③最后:有多个答案,输出 NOT UNIQUE

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值