[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 N≤2001。
- Subtask 2(65 pts):无特殊限制。
对于 100 % 100\% 100% 的数据, 2 ≤ N ≤ 2 × 1 0 6 + 1 2 \le N \le 2 \times 10^6+1 2≤N≤2×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");
}
}