[BalticOI 2014 Day1] Three Friends

2 篇文章 0 订阅
1 篇文章 0 订阅

[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
7
ABXCABC
输出 #1
ABC
输入 #2
6
ABCDEF
输出 #2
NOT POSSIBLE
输入 #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

原题链接

题解

思路

这道题首先就说了, 是把原串复制一遍,然后再插入一个字符, 从而得到新串。所以我们可以把新串分为前一部分和后一部分, 而插入的字符只可能在这两部分中的一个部分。那么, 我们只要分类讨论字符在这两个部分的情况是否成立就行了。如果都不成立就直接NOT POSSIBLE, 如果其中一个成立就输出成立的时候没有插入字符的那一个部分, 如果都成立就输出NOT UNIQUE
其中判断插入有两种方法。

法一:hash

我们可以分别枚举插入字符的位置, 然后通过hash公式进行计算
代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define MAXN 2000001

char c[MAXN + 5];

class hash {
	private:
		unsigned long long h[MAXN + 5];
		
	public:
		int size;
		unsigned long long b[MAXN + 5];
		
	public:
		void structure (char * c, int n) {//构造
			b[0] = 1;
			h[0] = 0;
			size = n;
			
			for (int i = 1; i <= n; i ++) {
				b[i] = b[i - 1] * 1331;
				h[i] = h[i - 1] * 1331 + c[i - 1];
			}
		}
		
		int structure (char * c) {
			int n = strlen (c);
			
			structure (c, n);
			
			return n;
		}
		
		unsigned long long access (int l, int r) {//求区间hash值
			if (r < l) {
				return 0;
			}
			
			return h[r] - h[l - 1] * b[r - l + 1];
		}
		
		unsigned long long access () {
			return h[size];
		}
		
		unsigned long long access (int l, int k, int r) {//求排除位置k后的hash值
			return access (k + 1, r) + access (l, k - 1) * b[r - k];
		}
}sa;

int main () {
	int n, m;
	bool bl = 0;
	
	scanf ("%d", &n);
	
	m = (n + 1) / 2;
	
	if (n & 1 == 0) {
		printf ("NOT UNIQUE");
		
		return 0;
	}
	
	scanf ("%s", c + 1);
	
	sa.structure (c + 1);
	
	for (int i = 1; i <= m; i++) {//分别枚举插入位置
		if (sa.access (1, i, m) == sa.access (m + 1, n)) {
			bl = 1;
		} 
	}
	
	for (int i = 1; i <= m; i++) {
		if (sa.access (m, m + i, n) == sa.access (0, m - 1)) {
			if (bl == 1 && sa.access (0, m - 1) != sa.access (m + 1, n)) {
				printf ("NOT UNIQUE");
				
				return 0;
			}
			
			c[m] = '\0';
			
			printf ("%s", c + 1);
			
			return 0;
		} 
	}
	
	if (bl) {
		printf ("%s", c + m + 1);
		
		return 0;
	}
	
	printf ("NOT POSSIBLE");
}
法二:双指针

用两个指针记录两部分, 如果不一样就停止设为没有插入元素的区间, 如果在一种情况中停止两次, 就设这种情况不成立。
代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define MAXN 2000001

char c[MAXN + 5];

int main () {
	int n;
	bool bl1 = 1, bl2 = 1;
	
	scanf ("%d", &n);
	scanf ("%s", c + 1);
	if (!(n & 1)) {
		printf ("NOT POSSIBLE");
		
		return 0;
	}
	for (int i = 1, j = (n >> 1) + 2, k = 1; i <= (n >> 1) + 1; i ++, j ++) {
		if (c[i] != c[j]) {
			if (k <= 0) {
				bl1 = 0;
				break;
			}
			else {
				k --;
				j --;//停滞没有插入元素区域的指针
			}
		}
	}
	for (int i = 1, j = (n >> 1) + 1, k = 1; j <= n; i ++, j ++) {
		if (c[i] != c[j]) {
			if (k <= 0) {
				bl2 = 0;
				break;
			}
			else {
				k --;
				i --;
			}
		}
	}
	if (bl1 == 1 && bl2 == 1) {
		bool bl = 1;
		
		for (int i = 1, j = (n >> 1) + 2; i <= (n >> 1) && j <= n; i ++, j ++) {
			if (c[i] != c[j]) {
				bl = 0;
				break;
			}
		}
		if (bl) {
			printf ("%s", c + (n >> 1) + 2);
		}
		else {
			printf ("NOT UNIQUE");
		}
	}
	else if (bl1 == 1 && bl2 == 0) {
		printf ("%s", c + (n >> 1) + 2);
	}
	else if (bl1 == 0 && bl2 == 1) {
		c[(n >> 1) + 1] = '\0';
		printf ("%s", c + 1);
	}
	else if (bl1 == 0 && bl2 == 0) {
		printf ("NOT POSSIBLE");
	}
} 
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值