1459:friends
有三个好朋友喜欢在一起玩游戏,A 君写下一个字符串 S,B 君将其复制一遍得到 T,C 君在 T 的任意位置(包括首尾)插入一个字符得到 U。现在你得到了 U,请你找出 S。
【输入】
第一行一个数 N,表示 U 的长度。 第二行一个字符串 U,保证 U 由大写字母组成。
【输出】
输出一行,若 S 不存在,输出 NOT POSSIBLE。若 S 不唯一,输出 NOT UNIQUE,否则输出 S。
【输入样例】
7
ABXCABC
【输出样例】
ABC
【提示】
样例输入2:
6
ABCDEF
样例输出2:
NOT POSSIBLE
样例输入3:
9
ABABABABA
样例输出3:
NOT UNIQUE
数据范围:
2≤N≤2000001。
题解:
打完省赛,发现字符串hash不是很熟,就刷了一下hash的题,下次还不会就真的barbecue了。
主要是用进制哈希的思想,枚举插入字符的位置X,然后对比两半的子串哈希值是否相等。相等就是我们要找的子串。
进制哈希公式:
(1)区间的哈希值Hash【L,R】
ans=Hash[R]-Hash[L-1] x power[R-L+1] ;
(2)不连续的子串哈希值Hash【L,X-1】【X+1,R】
ans=Hash【L,X-1】*power[R-X]+hashs【X+1,R】
(一定要主意区间的范围,我找bug找了半小时,我T*真的会谢!别忘了点赞哦 栓Q!)
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const ull M=13331;
const int maxn=2e6+5;
ull power[maxn],_hash[maxn];
void into()
{
power[0]=1;
for(int i=1;i<=maxn;i++)
power[i]=power[i-1]*M;
}
ull Hash(int l,int r)
{
return _hash[r]-_hash[l-1]*power[r-l+1];
}
int main()
{
into(); //先初始化计算power;
int n;
cin>>n;
string a;
cin>>a;
a=" "+a; //下标从1开始,下面取范围方便一下
if(n%2==0)
{
printf("NOT POSSIBLE\n");return 0; //如果长度是偶数的话,那可能没有插入字符
}
string s1=" ",s2=" ";
int flag=0;
_hash[0]=0;
for(int i=1;i<=n;i++){
_hash[i]=_hash[i-1]*M+a[i]-'A'+13;
}
//假设插入的字符在x的位置
// 如果x在左边
int mid=n/2+1;
ull sumr=Hash(mid+1,n); //计算右边子串的哈希值
for(int i=1;i<=mid;i++)
{
ull ans=Hash(1,i-1)*power[mid-i]+Hash(i+1,mid); //计算左边以x分割,不连续子串的哈希值
if(ans==sumr)
{
s1=a.substr(mid+1,mid-1); //截取右边的字符串
flag++;
break;
}
}
//如果x在右边
mid=n/2;
ull suml=Hash(1,mid); //左边子串的哈希值
for(int i=mid+1;i<=n;i++)
{
ull ans=Hash(mid+1,i-1)*power[n-i]+Hash(i+1,n); //计算右边以x分割,不连续子串的哈希值
if(ans==suml)
{
s2=a.substr(1,mid); // 截取左边的字符串
flag++;
break;
}
}
if(flag==0)
printf("NOT POSSIBLE\n");
else if(flag==1||s1==s2)
{
s1==" "? cout<<s2<<endl :cout<<s1<<endl; //如果左右两边求出的s子串相等,输出一个
}
else if(flag>=2)
cout<<"NOT UNIQUE\n";
}