题目
题目描述
给定一个字符串S,先将字符串S复制一次(变成双倍快乐 ),得到字符串T,然后在T中插入一个字符,得到字符串U。
给出字符串U,重新构造出字符串S。
所有字符串只包含大写英文字母。
输入格式
第一行一个整数N,表示字符串U的长度。
第二行一个长度为N的字符串,表示字符串U。
输出格式
一行一个字符串,表示字符串S。
特别地:
- 如果字符串无法按照上述方法构造出来,输出NOT POSSIBLE;
- 如果字符串S不唯一,输出 NOT UNIQUE。
样例
样例1输入
7
ABXCABC
样例1输出
ABC
样例2输入
6
ABCDEF
样例2输出
NOT POSSIBLE
样例3输入
9
ABABABABA
样例3输出
NOT UNIQUE
数据范围
2 N 2000001
分析
这一道题要用 hash。
为了减少hash冲突而*31。
因为S翻倍后要插入一个字符,所以始终是2n+1,也就是奇数,所以如果是偶数就输出NOT POSSIBLE,并结束。
插入的字符有2种情况:
- 插入的字符在前面一半之间,则S(也就是答案)就在后面一半,我们就在前面枚举字符的位置,然后根据 hash值来判断是否相等;
- 插入的字符在后面一半之间,则S(也就是答案)就在前面一半,我们就在后面枚举字符的位置,然后根据 hash值来判断是否相等;
AC代码+注释
#include<bits/stdc++.h>
using namespace std;
long long n,hash[2000005],s[2000005],sum=0,ans,x;
char u[2000005];
map<long long,bool> flag;
int main()
{
scanf("%lld %s",&n,u);
if(!(n%2))
{
printf("NOT POSSIBLE");
return 0;
}
//因为S翻倍后要插入一个字符,所以始终是2n+1,也就是奇数,所以如果是偶数就输出NOT POSSIBLE,并结束
s[0]=1;
for(long long i=1;i<n;i++)
{
s[i]=s[i-1]*31;
//*31是为了减少hash冲突
}
for(long long i=0;i<n;i++)
{
hash[i+1]=hash[i]*31+(long long)(u[i]-'A'+1);
}
x=n>>1;
for(long long i=1;i<=x;i++)
{
if(hash[x+1]-hash[i]*s[x+1-i]+hash[i-1]*s[x+1-i]==hash[n]-hash[x+1]*s[x]&&!flag[hash[n]-hash[x+1]*s[x]])
{
sum++;
//找到了插入的字符也就自然有一个答案
ans=i;
flag[hash[n]-hash[x+1]*s[x]]=1;
//为了后面不再重复找到,flag便置为1,后面便进不了循环
}
}
//插入的字符在前面一半之间,则S(也就是答案)就在后面一半,我们就在前面枚举字符的位置,然后根据 hash值来判断是否相等;
if(hash[x]==hash[n]-hash[x+1]*s[x]&&!flag[hash[x]])
{
sum++;
ans=x+1;
flag[hash[x]]=1;
}
for(long long i=x+2;i<=n;i++)
{
if(hash[n]-hash[i]*s[n-i]+(hash[i-1]-hash[x]*s[i-1-x])*s[n-i]==hash[x]&&!flag[hash[x]])
{
sum++;
ans=i;
flag[hash[x]]=1;
}
}
//在后面则相反
if(!sum)
{
printf("NOT POSSIBLE");
}
//没有答案输出NOT POSSIBLE
else
{
if(sum>1)
{
printf("NOT UNIQUE");
}
//如果答案不唯一,输出NOT UNIQUE
else
{
if(ans<=x+1)
{
for(long long i=x+2;i<=n;i++)
{
printf("%c",u[i-1]);
}
}
//如果ans比U的一半少就输出后面一半
else
{
for(long long i=0;i<x;i++)
{
printf("%c",u[i]);
}
}
//否则相反
}
}
return 0;
}