学奥一本通1459:friends BalticOI 2014

1459:friends

【题目描述】 原题来自:BalticOI 2014

有三个好朋友喜欢在一起玩游戏,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";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

giao源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值