51 nod-2619 三个好朋友
题目
首先有一个字符串 S,两个S拼接变成字符串 T,接着在 T 中插入一个字符变成了字符串 U。题目给出 U串,求是否存在原 S串。
输出一行,若S不存在,输出"NOT POSSIBLE".若S不唯一,输出"NOT UNIQUE".否则输出S。
分析
想暴力思路,对于每一位都假设是添加进去的,模拟删除之后两个字符串的左右是否相同。复杂度 O(n^2),肯定不行。
复杂度主要在比较字符串上,现在对整个字符串进行HASH,比较左右两部分时只比较HASH值,比较一次 O(1)。总的O(n)。
例:AGEDEF 哈希值(_hash数组存的东西):
A x 5 + G x 4 + E x 3 + D x 2 + E x + F Ax^5 + Gx^4 + Ex^3 + Dx^2 + Ex + F Ax5+Gx4+Ex3+Dx2+Ex+F
x 是自己指定的值。
注意取哈希值的时候,假设pos位是添加进去的,pos < mid。那么删除之后的右部分直接可以用预处理的哈希值。删除之后的左部分又被 pos 分了两部分,其中一部分要处理一下。
所以按照pos在左 中 右=分别讨论即可。
(坑:题目最后一组数据大于2e5)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll mod = 9999991;
const ull base = 23333;
const int N = 2e6 + 10;
ull _hash[N], p[N], pre; // 用 ull 自然溢出
int n, cnt;
char str[N], ans[N];
inline ull get(int l, int r){
return _hash[r] - _hash[l - 1] * p[r - l + 1];
}
inline int cal(int pos){
ull l, r;
int mid = (n >> 1) + 1;
if(pos < mid){
l = get(1, pos - 1) * p[mid - pos] + get(pos + 1, mid);
r = get(mid + 1, n);
}else if(pos == mid){
l = get(1, mid - 1);
r = get(mid + 1, n);
}else{
l = get(1, mid - 1);
r = get(mid, pos - 1) * p[n - pos] + get(pos + 1, n);
}
if(l == r){
if(pre == l) //跟之前答案一样
return 0;
pre = l;
if(pos <= mid){
for(int i = mid + 1; i <= n; i++){
ans[i - mid] = str[i];
}
}else{
for(int i = 1; i < mid; i++){
ans[i] = str[i];
}
}
return 1;
}
return 0;
}
int main()
{
scanf("%d%s", &n, str + 1);
if(!(n&1)){
puts("NOT POSSIBLE");
return 0;
}
p[0] = 1;
for (int i = 1; i <= n; i++){ //预处理 HASH值
p[i] = p[i - 1] * base;
_hash[i] = _hash[i - 1] * base + str[i];
}
for(int i = 1; i <= n; i++){
cnt += cal(i);
if(cnt > 1)
break;
}
if(!cnt){
puts("NOT POSSIBLE");
}else{
puts(cnt == 1 ? ans + 1 : "NOT UNIQUE");
}
return 0;
}