P6739 [BalticOI 2014 Day1] Three Friends

[BalticOI 2014 Day1] Three Friends

题目描述

有一个字符串 S S S,对他进行操作:

  1. S S S 复制为两份,存在字符串 T T T
  2. T T T 的某一位置上插入一个字符,得到字符串 U U U

现在给定 U U U,求 S S S

输入格式

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

输出格式

  • 如果不能通过上述的步骤从 S S S 推到 U U U,输出 NOT POSSIBLE
  • 如果从 U U U 得到的 S S S 不是唯一的,输出 NOT UNIQUE
  • 否则,输出一个字符串 S S S

样例 #1

样例输入 #1

7
ABXCABC

样例输出 #1

ABC

样例 #2

样例输入 #2

6
ABCDEF

样例输出 #2

NOT POSSIBLE

样例 #3

样例输入 #3

9
ABABABABA

样例输出 #3

NOT UNIQUE

提示

数据规模与约定

本题采用捆绑测试。

  • Subtask 1(35 pts): N ≤ 2001 N \le 2001 N2001
  • Subtask 2(65 pts):无特殊限制。

对于 100 % 100\% 100% 的数据, 2 ≤ N ≤ 2 × 1 0 6 + 1 2 \le N \le 2 \times 10^6+1 2N2×106+1,保证 U U U 中只包含大写字母。

说明

翻译自 BalticOI 2014 Day1 B Three Friends

大致思路

首先一个很显然的结论:由于 U 串是 S 串复制两次后插入一个字符所得,因此长度必然为奇数。所以特判:若 n 为偶数则直接输出 NOT POSSIBLE。
接下来,将得到的字符串分成两部分 1∼mid, mid+1∼n,并分别记录这两段的哈希值。然后对于 mid+1∼n,枚举删去 1∼mid 中的字符,若所得字符串与后半段相等则答案数 ans+1 并跳出循环。反之亦然。

inline unsigned long long check(int l, int r) { 
	return h[r]-h[l-1]*pre[r-l+1]; 
}//区间哈希值
inline unsigned long long sum(int l, int r, int k) { 
	return check(l,k-1)*pre[r-k]+check(k+1,r); 
}//删去字符
l1=check(mid+1,n);
    for(int i=mid+1;i<=n;i++)
		a.push_back(s[i]);
    for(int i=1;i<=mid;i++){
        l2=sum(1,mid,i);
        if (l1==l2){
            ans++;
            c=a;
            break;
        }
    }
    l2=check(1,mid-1);
    for(int i=1;i<mid;i++) 
		b.push_back(s[i]);
    for(int i=mid;i<=n;i++){
        l1=sum(mid,n,i);
        if(l1==l2) {
            ans++;
            d=b;
            break;
        }
    }//枚举

然后进行判断:

  • 若答案数为零,则无解,输出 NOT POSSIBLE。

  • 若答案数为一,或删去字符后的两串相等,则输出删去后不为空的字符串(即所求的 S)。

  • 否则说明答案不唯一,输出 NOT UNIQUE。

AC CODE

#include<bits/stdc++.h>
using namespace std;
#define maxn 2000002
string a, b, c, d;
int n, mid, l1, l2, h[maxn], pre[maxn], ans;
char s[maxn];
inline unsigned long long check(int l, int r) { 
	return h[r]-h[l-1]*pre[r-l+1]; 
}//区间哈希值
inline unsigned long long sum(int l, int r, int k) { 
	return check(l,k-1)*pre[r-k]+check(k+1,r); 
}//删去字符
int main(){
    scanf("%d%s",&n,s+1);
    mid=(n+1)>>1;
    if (n%2==0){
    	cout<<"NOT POSSIBLE";
		return  0;
	}//特判
    pre[0]=1;
    for(int i= 1;i<=n;i++){
        pre[i]=pre[i-1]*10007;
        h[i]=h[i-1]*10007+(s[i]-'A'+1);
    }//预处理
    l1=check(mid+1,n);
    for(int i=mid+1;i<=n;i++)
		a.push_back(s[i]);
    for(int i=1;i<=mid;i++){
        l2=sum(1,mid,i);
        if (l1==l2){
            ans++;
            c=a;
            break;
        }
    }
    l2=check(1,mid-1);
    for(int i=1;i<mid;i++) 
		b.push_back(s[i]);
    for(int i=mid;i<=n;i++){
        l1=sum(mid,n,i);
        if(l1==l2) {
            ans++;
            d=b;
            break;
        }
    }//枚举
    if(!ans)
        cout<<"NOT POSSIBLE";
    else if(ans==1||c==d){
    	if(c.size()==0)cout<<d;
    	else cout<<c;
	}
    else cout<<"NOT UNIQUE";//判断输出
    return 0;
}
附上封面~

请添加图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值