【BZOJ3916】friends(hash+分情况讨论)

题目地址:https://www.lydsy.com/JudgeOnline/problem.php?id=3916

题目:


Description

有三个好朋友喜欢在一起玩游戏,A君写下一个字符串S,B君将其复制一遍得到T,C君在T的任意位置(包括首尾)插入一个字符得到U.现在你得到了U,请你找出S.

Input

第一行一个数N,表示U的长度.

第二行一个字符串U,保证U由大写字母组成

Output

输出一行,若S不存在,输出"NOT POSSIBLE".若S不唯一,输出"NOT UNIQUE".否则输出S.

Sample Input

Sample Input1:
7
ABXCABC
Sample Input2:
6
ABCDEF
Sample Input3:
9
ABABABABA

Sample Output

Sample Output1:
ABC
Sample Output2:
NOT POSSIBLE
Sample Output3:
NOT UNIQUE

HINT

对于100%的数据 2<=N<=2000001

解题思路:


n为偶数:NOT POSSIBLE,因为按照题意,n应该为奇数

令half = n / 2,讨论插入的字母在左侧,中间,右侧三种情况,每次判断两个子串(或子序列)的hash值是否相同,不统计相同的S

例如:字符串aaabaab

i1234567
s[i]aaabaab

遍历i, 插入的字母在i位置上。当i=1时,两个串s1="aab" s2="aab' 相同,所以可知S="aab", 当i=2时,s1="aab",s2="aab",s1=s2,S'="aab",S'=S,所以重复统计,这种情况不算NOT UNIQUE。

如何求s[1]s[2]s[4]s[5]这四个字母组成的字符串的hash值呢?

其实字符串hash相当于把字符串映射成一个p进制的数

s[1]s[2]组成的hash值=hash[2], s[4]s[5]组成的hash值= hash[5] - hash[3] * p^(5-3)

所以s[1]s[2]s[4]s[5]对应的hash值= hash[2] * p^(5-3) + hash[5] - hash[3] * p^(5-3)

把表格里的a,b换成1,2,映射成10进制的数想一想就懂啦?️下标不要写错!

 

ac代码:


并没有题目给的30s那么吓人?

#include <bits/stdc++.h>
using namespace  std;
typedef unsigned long long ll;
const ll maxn = 2000010;
const ll  p = 2333323;
ll has[maxn], power[maxn];
char s[maxn];
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int n;
    scanf("%d", &n);
    scanf("%s", s + 1);
    if(n % 2 == 0)
    {
        printf("NOT POSSIBLE\n");
        return 0;
    }
    has[0] = 0, power[0] = 1;
    for(int i = 1; i <= n; i++)
    {
        power[i] = power[i - 1] * p;
        has[i] = has[i - 1] * p + (ll)(s[i] - 'A' + 1);
    }
    int half = n / 2, pos = 0;
    ll ans = 0;
    for(int i = 1; i <= n; i++)
    {
        if(i <= half)
        {
            ll l = has[i - 1] * power[half + 1 - i] + has[half + 1] - has[i] * power[half + 1 - i];
            ll r = has[n] - has[half + 1] * power[half];
            if(l == r)
            {
                pos = i;
                if(!ans) ans = l;
            }
        }
        else if(i == half +1)
        {
            ll l = has[half];
            ll r = has[n] - has[half + 1] * power[half];
            if(l == r)
            {
                pos = i;
                if(!ans) ans = l;
                else if(ans != l)
                {
                    printf("NOT UNIQUE\n");
                    return 0;
                }
            }
        }
        else // i > half + 1
        {
            ll l = has[half];
            ll r = (has[i - 1] - has[half] * power[i - half -1]) * power[n - i] + has[n] - has[i] * power[n - i];
            if(l == r)
            {
                pos = i;
                if(!ans) ans = l;
                else if(ans != l)
                {
                    printf("NOT UNIQUE\n");
                    return 0;
                }
            }
        }
    }
    if(ans == 0) printf("NOT POSSIBLE\n");
    else
    {
        if(pos <= half + 1)  //输出右侧
            printf("%s\n",s + half + 2);
        else
        {
            for(int i = 1; i <= half; i++) //输出左侧
                printf("%c",s[i]);
        }
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值